aghost
io.github.Thinking-Sound-LabPublished by GitHub account Thinking-Sound-Labv1.0.0Updated Nov 7, 2026
Schedule posts to Instagram, Facebook, LinkedIn, X, Threads and YouTube, and automate Instagram DMs
Overview
Remote MCP server for aghost that lets an assistant schedule social posts and manage Instagram comment-to-DM automations, inbox and analytics.
- What you can do
- Create, edit, pause, activate and delete Instagram comment-to-DM automations
- Schedule, edit and cancel Instagram posts together with the automations that answer them
- Inspect statistics and delivery activity for automations and posts
- Read connected accounts, posts, contacts and inbox conversations
- Send inbox messages on the connected Instagram account
- When to use it
- Use it when you run Instagram engagement through aghost and want an assistant to build or adjust DM automations, queue posts, check delivery stats, or read and reply to inbox conversations without opening the dashboard.
- Requirements
- Remote endpoint at aghost.app over streamable HTTP. Authentication is OAuth (required) or an Authorization header carrying a Bearer API key created under Settings, API. A paid workspace plan is needed: the dashboard, REST API and MCP are gated behind a subscription, and Instagram features additionally require the workspace's Instagram account to be connected.
Installation
In SourceWeft
- Open aghost in the dashboard and add it to a workspace.
- Connect the required credentials. They are stored privately and never shown on public pages.
- Enable the server for the chats that should use its tools.
Web executable via Streamable HTTP. Remote servers run from the web runtime once configured in a workspace.
Use with an agent
Paste this prompt into Claude Code, Codex, Cursor or another agent — or into SourceWeft chat. The agent reads this server's install guide, shows you its publisher, endpoint and sign-in, and adds it to your client once you agree. Keys never go into a command.
Read https://sourceweft.com/mcp/io.github.Thinking-Sound-Lab%2Faghost/install.md and connect the MCP server it describes to my client. Before changing any config, show me its publisher, endpoint and sign-in method and wait for my OK. Never put an API key in a command or in a file I would commit; ask me where it should come from.Other MCP clients
Add this to your client's mcpServers config. The client completes OAuth sign-in on first connect.
{
"mcpServers": {
"aghost": {
"type": "http",
"url": "https://aghost.app/api/mcp"
}
}
}README
aghost
Agentic DM automation for Instagram.
Create, manage and monitor comment-to-DM automations, and schedule the Instagram posts they answer, through MCP, the REST API, or the dashboard. The dashboard also cross-posts to Facebook, LinkedIn, X, Threads and YouTube, with analytics for each channel. MCP and API access are included on every plan.
Stack · Architecture · Run locally · Configuration · API · MCP · Analytics · Brand · Search and AI assistants · Testing · Deployment
Stack
Architecture
- One origin for the browser. Pages and
/apishare the web app's origin; Next.js rewrites/api/*to the API. Locally, the same proxy points at the API in Docker. - UI in the app, rules in the package. Business rules and provider calls live in
packages/api. The web app renders UI and calls the API through its typed client. - Tenant isolation. Supabase row-level security scopes reads to a workspace, and the API verifies the user before privileged writes. Account webhook updates and their deduplication ledger entry commit in one transaction.
- No credentials in the browser. Provider keys stay in the API. Session cookies are HttpOnly and use Secure on HTTPS origins.
- Agent access. REST and MCP have separate route modules and Settings tabs. Workspace API keys authenticate both interfaces, which share business operations, validation, and permissions. Key hashes are backend-only; full tokens are shown once.
Run locally
You need Node.js 22 or newer, pnpm, and Docker with Docker Compose.
The first run stops and asks for a Google OAuth client: see Google sign-in.
pnpm dev prepares the root .env, starts Supabase in Docker and applies pending migrations, starts the API in Docker, and then starts Next.js on the host. API changes reload inside the container; web changes use hot reload.
Closing the web process leaves the Docker services running; pnpm local:stop stops them and keeps the database.
[!NOTE] The stack uses fixed local ports, so run it in one workspace at a time. The Supabase CLI manages its containers from
supabase/config.toml, andcompose.yamlruns the API on Supabase's Docker network.
Commands
Google sign-in
Google is the only way to sign in, and a first sign-in creates the account. Supabase does the exchange with Google, so it holds the OAuth client:
- In the Google Cloud console, create an OAuth client ID of type Web application.
- Add
http://127.0.0.1:54321/auth/v1/callbackto its authorized redirect URIs. - Set
SUPABASE_AUTH_EXTERNAL_GOOGLE_CLIENT_IDandSUPABASE_AUTH_EXTERNAL_GOOGLE_SECRETin the root.env.
Supabase reads both values when it starts. After changing them, or after switching from a branch with different sign-in settings, run pnpm local:stop and then pnpm dev.
Provider webhooks locally
Zernio notifies the API when an Instagram account connects or disconnects, and when messages are sent or received. Message events schedule and cancel follow-up nudges. To receive those events on your machine, run:
This starts a Cloudflare quick tunnel in Docker, which needs no Cloudflare account. Only /api/webhooks/zernio and /api/webhooks/dodo are public; Cloudflare answers 404 for every other path. With Zernio configured, the command:
- Generates
ZERNIO_WEBHOOK_SECRETin the root.envif it is empty. - Creates or updates a Zernio webhook named
aghost local tunnelthat points to the tunnel and subscribes toaccount.connected,account.disconnected,message.sent, andmessage.received. - Asks Zernio to send a test event through the tunnel, and fails if it is not delivered.
Set ZERNIO_API_KEY or a Dodo test-mode API key. With Dodo configured, the command also registers its local subscription/payment webhook, stores the signing secret in the root .env, and recreates the API container. Deliveries and their status codes appear in pnpm local:logs; a 401 means the signature did not match the secret.
The trycloudflare.com address changes each time the tunnel starts, so run pnpm local:tunnel again after pnpm local:stop or a Docker restart. While the tunnel is stopped, deliveries to the old address fail; you can resend them from Zernio's webhook logs. Connecting an account does not depend on the tunnel: after authorization, /accounts/callback reads the account list from Zernio directly.
Configuration
.env at the repository root is the only local configuration file. Its template is .env.example. pnpm dev and pnpm setup:local create it, fill in the local Supabase keys, and never overwrite values you set. Do not add env files under apps/server or apps/web.
The dashboard, REST API and MCP need a plan: after sign-in the app shows only the paywall until the workspace subscribes, so configure Dodo to use it locally. To connect channels, also set ZERNIO_API_KEY. Pro, Growth and Business cost $29 / $39 / $49 per month for 1 / 2 / 10 social sets, each one account on every platform, billed in US dollars in every country with taxes included. Yearly billing is about 20% less, paid upfront. See pricing and billing setup and channels. Without Dodo credentials, sign-in works and nobody can subscribe. Without Zernio credentials, Instagram features are unavailable; the app never substitutes sample accounts or analytics.
- Only
APP_URL,API_URLand the PostHog key and host reach Next.js; the PostHog key is public by design. Supabase, Zernio and Dodo credentials go to the API container only, and the Google client to Supabase only. Env files are ignored by Git and excluded from Docker images. - Compose gives the API container
http://supabase_kong_aghost:8000asSUPABASE_URL, so it talks to Supabase over the Docker network. - For a public or tunneled setup, update
APP_URLand allow/api/auth/callbackin Supabase's redirect URLs. Zernio redirects authorized Instagram connections to/accounts/callback. - Deployed environments inject these variables directly; no env file is needed.
API
Open Settings → API, click + API key, and name your workspace key in the dialog. After creation, copy and save the key before closing the dialog; the full key will not be shown again. Agents can create, edit, pause, activate, and delete automations; schedule, edit, and cancel Instagram posts with their automations; inspect statistics and delivery activity; read accounts, posts, contacts, and inbox conversations; and send inbox messages.
Call <APP_URL>/api/v1 with Authorization: Bearer <key>. New keys have full agent access and no expiry; revoke them in Settings to stop access. GET /api/v1/openapi.json provides the REST API contract. See the REST API guide for requests and deployment requirements.
MCP
Open Settings → MCP for your endpoint and client configuration. Copy skill gives Claude Code, Codex, Cursor, or another agent instructions to configure the connection. Use <APP_URL>/api/mcp with an HTTP MCP client and a key created in the API tab. New keys can use every Aghost agent tool. Apps that sign in to MCP servers, such as Claude's custom connectors, need only the URL: the owner approves the app in aghost, and it receives its own key.
MCP has its own backend route module on the same server as the API. See the MCP guide for client setup, available tools, and examples.
Analytics
PostHog records the product end to end. The browser records page views, clicks, web vitals, errors and session replays through a same-origin /relay. The API records what happened, whether in the dashboard, through an agent over REST or MCP, or by webhook: sign-ups, checkouts, subscription changes and revenue, account connections, automations, and every agent tool call. Browser and API events share the signed-in user's id and are grouped by workspace. Instagram users' messages and contacts stay out of replays.
Set POSTHOG_KEY and POSTHOG_HOST to turn it on. See the analytics guide for production setup, the event catalog, and the questions it answers.
Brand
The mascot is the logo. mascot.png is the one source image; the favicon, the Apple icon, and the share image in apps/web/app are made from it. The dashboard uses Inter. The marketing pages use Anton headings and Caveat handwritten notes, with Inter for body text; Next.js serves these fonts locally.
The colours are Tailwind tokens in globals.css: bg-brand, bg-brand-blue, text-brand-gray, and so on. Buttons on green carry black text, because white on Aghost Green is too faint to read. The marketing site adds ruled paper, graph paper, pastel notes, and taped product previews through scoped styles in notebook.css. The dashboard stays neutral and carries only the logo.
Search and AI assistants
The marketing pages serve readable HTML to search engines and AI assistants such as ChatGPT, Claude, and Perplexity. They describe Instagram comment-to-DM automation, its requirements, and the supported features. The Instagram DM automation guide explains where aghost fits into social media management: it schedules Instagram posts together with the automations that answer them, and does not manage other social networks.
- One copy of the text.
content.tsholds the facts, steps, features, and questions. The page, its structured data, and/llms.txtall read it, so crawlers are told what visitors see. - Agent access. The homepage, feature demo, and dedicated agent page explain how Claude Code, Codex, Cursor, or another MCP client can run aghost.
- Answers first. The guide opens with a product definition and uses descriptive headings for setup, use cases, requirements, and stats. Its footer and feature-page links make it discoverable without JavaScript.
- Structured data. The homepage describes the visible content as an
Organization,WebSite,WebApplication, andFAQPagein JSON-LD. The guide addsWebPageandBreadcrumbListdata matching its visible breadcrumb. Neither claims reviews or ratings. - Crawling.
robots.tsadmits public-page crawlers, with explicit groups for Googlebot, Bingbot, OpenAI search, Claude search, Perplexity search, and their user-requested fetchers. Each group excludes/api/, except the public setup skill at/api/mcp/skill.md, and the analytics relay at/relay/.sitemap.tslists the public pages. Signed-in pages answer withnoindex. /llms.txt. A plain-text summary for assistants that choose to read one. It links to product explanations, features, current pricing, and support before the agent setup details. It is a convenience, not a prerequisite for indexing or a citation guarantee.
Canonical links, the sitemap, and the structured data use the site's public origin: APP_URL locally, and the project's production domain on Vercel.
After publishing, check that the production origin is correct, public pages return HTTP 200 without login or a bot challenge, and the CDN or firewall permits verified search crawlers. Submit the sitemap in Google Search Console and Bing Webmaster Tools, then inspect indexing and crawler logs. Local checks cannot establish production indexing or citations.
The implementation follows OpenAI's crawler guidance, Anthropic's crawler guidance, Perplexity's crawler guidance, and Google's guidance for AI features. These services choose which sources to index and cite. Crawl access and helpful content improve eligibility; they do not guarantee appearance for any keyword.
Project structure
Testing
pnpm verify runs the integration tests against the local Supabase stack, so start it first with pnpm setup:local or pnpm dev. CI uses pnpm setup:test instead, which starts only what the tests need. Provider adapter tests use a local HTTP fixture and never send real Instagram messages. The infrastructure tests synthesize both CDK stacks offline.
pnpm check:unused also runs in CI. Its Knip configuration includes the billing and CDK command-line entry points, preserves generated Supabase helpers, and accounts for PostCSS and CDK dependencies loaded through configuration.
The OAuth registration saturation test fills the global one-minute budget, so it is skipped on a shared local stack. With .env pointing to a dedicated local Supabase database, run AGHOST_TEST_ISOLATED_DATABASE=1 pnpm test to include it. CI enables it on its fresh database; the other OAuth tests run without the flag.
With the app running, pnpm verify:local checks pages, the Next.js proxy, and the start of Google sign-in: the HttpOnly PKCE cookie, the redirect to Google with the client from the root .env, and the return to the login page when a sign-in does not finish. Signing in with a Google account, browser interaction, and visual review are separate checks.
Live OAuth and comment-to-DM delivery need Zernio credentials and an authorized professional Instagram account.
Deployment
A push to main releases the backend in one job, so a release needs one approval and its steps run in order: the schema, then the API that uses it. Only main can release; other branches are refused by the workflow and by the prod environment. An unchanged API deploys as a no-op: its image is built from the server's packages only, so the CDK finds it already in ECR and CloudFormation finds no changes.
Web on Vercel
The marketing site and dashboard are one Next.js app. Vercel's Git integration builds it: a preview for every pull request, and production on every push to main. No Vercel credentials are stored in GitHub.
Production is served at https://aghost.app; www.aghost.app redirects to it.
[!NOTE] Vercel and the Release job start from the same push and finish independently, usually Vercel first. Keep an API change compatible with the web app that is already live, or merge the API change first.
The Vercel CLI does not read .gitignore, so .vercelignore keeps local env files out of a deploy made by hand from a developer's machine.
API on AWS
infra/ defines the API with the AWS CDK:
- Compute. x86_64 Fargate tasks, from two to four as CPU demands. Autoscaling owns the task count, so a deployment never resets it.
- Address. An API Gateway HTTPS endpoint, which needs no domain, DNS record, or certificate. It reaches a private load balancer through a VPC link.
- Network. The account's default VPC. Tasks get public addresses for their outbound calls to Supabase, Zernio, and ECR; their security group admits traffic only from the load balancer, and the load balancer only from API Gateway.
- Image. Built from
turbo prune server --dockeroutput: the server's workspace packages and their dependencies, nothing else. - Secrets. Supabase and Zernio credentials come from Secrets Manager at task start.
- Releases. New tasks must pass
/api/healthbefore old ones stop, and the circuit breaker rolls back a release that never becomes healthy. Logs are kept for 30 days. - Access. GitHub Actions signs in through OIDC, without stored AWS keys. Its role can only use the roles that
cdk bootstrapcreated.
One-time setup
1. AWS. From the repository root and with administrator credentials, run:
The deployment prints DeployRoleArn for step 3 and ApiUrl, the API's address, for step 2.
[!NOTE] Skip
cdk bootstrapandcreate-open-id-connect-providerif the account already has them; the stacks use what exists. If the region's default VPC was deleted, recreate it withaws ec2 create-default-vpc.supabase projects api-keys --revealprints the Supabase keys; without--reveal, the secret key is masked and will not work.
2. Vercel. Create a project with the root directory apps/web, connect it to the GitHub repository, and add API_URL=<ApiUrl> to its Production and Preview environments. APP_URL is the address Vercel serves the project at. The web app reads that address from Vercel's own VERCEL_PROJECT_PRODUCTION_URL, so the project needs no APP_URL of its own; set one only to publish a different address in canonical links.
3. GitHub. Under Settings → Environments, create prod, limit its deployment branches to main, and add:
Create the token in the Supabase dashboard under Account → Access Tokens. Scope it to this project only, with Database: Read-write. The Release job uses it to create a short-lived database role for the migrations, so no database password is stored.
GitHub's runners have no IPv6, and the database's direct address is IPv6-only, so migrations connect through the IPv4 session pooler. Its address has no password: postgresql://postgres.<project>@<pooler-host>:5432/postgres, shown in the dashboard under Connect → Session pooler, or saved to supabase/.temp/pooler-url by supabase link.
[!TIP] Add required reviewers to the
prodenvironment to approve each release before it starts.
4. Supabase, Google, and Zernio. In the Supabase dashboard, under Authentication, enable the Google provider with an OAuth client's ID and secret, disable the Email provider, and allow <APP_URL>/api/auth/callback in the redirect URLs. Add https://<project>.supabase.co/auth/v1/callback to that client's authorized redirect URIs in the Google Cloud console. Point the Zernio webhook to <ApiUrl>/api/webhooks/zernio with the same secret, subscribed to account.connected, account.disconnected, message.sent, and message.received. Apply 20261016090000_carousels_and_follow_ups.sql before the API release; see the feature rollout.
[!NOTE] Google sign-in returns to
APP_URL, and the API accepts writes only from its origin. Previews show the site and dashboard, but signing in from a preview does not work.
Changing the domain
Moving the site to another domain takes four steps, in this order. Sign-in keeps working at the old address until step 3 moves it to the new one.
- Vercel. Add the domain to the project under Settings → Domains. If Vercel's Production environment sets
APP_URL, update it to the new origin or remove it to useVERCEL_PROJECT_PRODUCTION_URL. Then redeploy production so canonical links, the sitemap, and/llms.txtuse the new domain. - Supabase. Under Authentication → URL Configuration, set the site URL to the new origin and add
<new origin>/api/auth/callbackto the redirect URLs. - GitHub. Set
APP_URLin theprodenvironment and run the Deploy workflow. The API then returns sign-ins, checkouts, and Instagram connections to the new origin, and accepts dashboard writes only from it. - The old address. Redirect its pages to the new origin with a routing rule on the Vercel project that leaves
/apiin place: host<old address>, pattern^/(?!api/)(.*)$, destination<new origin>/$1, status 308. Agents and webhooks configured with the old address keep working, because API keys and webhook signatures are not tied to an origin. Anchor the pattern; without^it also matches inside/apipaths.
Then add the new origin to the PostHog project's authorized URLs, and submit its sitemap to the search consoles.
Reviewing infrastructure changes
With AWS credentials and the variables from step 1, compare a change with what is deployed:
Synthesis looks up the default VPC in the account, so it needs credentials; the tests use a recorded copy and run offline.
Source: README.md at commit baff946
Tools
0Version history
1- v1.0.0LatestNov 7, 2026
Score
75% of the applicable checks met
How complete this listing is, computed from the registry entry, the endpoint and public sources. Not a rating of the server and not a statement that it is safe or working today: reachability is a dated check, and no installs or reviews go into the score.
- Reached in the last 30 daysRequired25 pts
- Auth declaredRequired10 pts
- Tools listed15 pts
Answer tools/list without a credential, or ship a package that starts in a sandbox without one.
- DescriptionRequired15 pts
- Logo10 pts
- Documentation15 pts
- License10 pts
Declare a license in the repository or the registry entry.