CEEJAYDocs

Connect an AI agent over MCP

Let Claude Code, Codex, Cursor or another MCP client read your community and draft changes, within a policy your admins control.

CEEJAY runs an MCP (Model Context Protocol) server. An AI agent connects to it and calls tools: it reads what the dashboard shows, such as analytics, members, tickets and signals, and, where the community allows it, makes a few changes in the dashboard.

Agents change the dashboard only. Messages, polls and giveaways are saved as drafts for your team to send, and nothing is posted in Discord. No tool deletes members, segments, reports or any other item, although one can remove a tag from a member.

This page is for the developer connecting the agent. Your community's team sets the rules on Settings → MCP server, described below.

The endpoint

MCP server URL
https://ceejay.communitystudios.xyz/api/mcp

That is the URL on the shared CEEJAY service. On a dedicated instance it is your dashboard's URL followed by /api/mcp. Settings → MCP server → Set up shows your URL with a copy button.

  • It takes POST only. The messages are JSON-RPC 2.0, and every request is handled on its own: there is no session to maintain, and a token you revoke stops working on its next call.
  • Send Content-Type: application/json and Accept: application/json, text/event-stream. Without both in Accept the endpoint returns 406. The response to a call can be a single server-sent event (event: message, then data: and the JSON), so use an MCP client library.
  • Request bodies can be up to 1 MB.
  • A caller that sends an Origin header, which browsers do and agents do not, must be your own dashboard. Anything else gets 403 Origin not allowed.
  • If the URL returns 404, MCP is not switched on for your instance. The Set up tab says so too.

Sign in, or use a token

Every call acts as a person, and an agent never sees more than that person can.

Sign in. Where your instance has agent sign-in switched on, the Set up tab shows commands that carry no token. The first time the agent connects, your browser opens. You choose which communities it can reach and what it may do, and from then on it acts as you. A client that connects without a credential gets 401 with a WWW-Authenticate header that points at /.well-known/oauth-protected-resource, and MCP clients use that to start the sign-in.

The Connected tab lists every agent that has signed in, with the permissions it was given and when it was approved and last used. The delete icon disconnects the agent, and it loses access immediately. A sign-in can cover several communities, each with its own policy.

Access token. For scripts and CI jobs that cannot open a browser, and where sign-in is not switched on:

Open Settings → MCP server → Tokens and press Create token.

Under Token name, give the token a name, such as Claude Code on my laptop.

Under Permissions, tick the ones to allow. Each says what it allows. Write permissions only appear when the community lets agents make changes, and a permission the community does not allow is greyed out.

Choose when it Expires: 30 days, 90 days (the default), 1 year or No expiry. Press Create token and copy the token. It is shown once.

A token starts with ceejay_mcp_, followed by 64 hexadecimal characters. It belongs to one community and acts as the person who made it. Their access is checked on every call, so if they lose access to the community the token stops working. The delete icon on the Tokens tab revokes it, effective immediately. It is not an API key: tokens and API keys are not interchangeable.

Connect a client

The Set up tab shows these with your URL filled in.

Claude Code. Run this, then type /mcp inside Claude Code, pick ceejay and sign in.

Claude Code: sign in
claude mcp add --transport http ceejay https://ceejay.communitystudios.xyz/api/mcp

Claude app. Open Settings, Connectors, Add custom connector. Paste the URL, then press Connect.

Codex. Run this, then codex mcp login ceejay to sign in.

Codex: sign in
codex mcp add ceejay --url https://ceejay.communitystudios.xyz/api/mcp

Cursor. Add this to .cursor/mcp.json. Cursor asks you to sign in when it first connects.

Cursor: sign in
{ "mcpServers": { "ceejay": { "url": "https://ceejay.communitystudios.xyz/api/mcp" } } }

To see the tools without a client, list them:

List the tools (cURL)
curl https://ceejay.communitystudios.xyz/api/mcp \
  -H "Authorization: Bearer $CEEJAY_MCP_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'

A call is the same with "method":"tools/call" and "params":{"name":"ceejay_get_pulse","arguments":{"range":"1w"}}. A tool returns readable text and, for most tools, a structured copy of the result.

What an agent may do

The community's policy decides, on Settings → MCP server → Policy. Everyone with access to the page can read it, and only server admins can change it.

SettingWhat it does
Allow AI appsThe master switch. Off, and agents can neither read nor change anything, even ones already connected. A connection is refused with 403 agents_disabled.
What agents can readOne tick per read permission. An agent cannot read anything that is not ticked.
Let agents make dashboard changes and draftsOff, and no change tool is available. On, the write permissions under it decide which ones are.
Requests per minuteThe most requests each connected agent can make in a minute, up to 120.

A community that has never saved a policy has agents allowed, every read permission on, changes off and 120 requests a minute.

Every call has to pass three checks: the community's policy, the permissions the token or sign-in was given, and the access of the person behind it. A token or sign-in can never widen the policy. A change to the policy takes effect within 30 seconds. An agent only sees the tools it could use.

Permissions

A write permission includes the matching read permission.

PermissionIn the dashboardAllows
community:readCommunity overviewThe server overview, health, signals that need attention, channels and roles
analytics:readAnalyticsEngagement, growth, retention, activity, timing, channels, keywords and invites
members:readMembers and segmentsSearch members, open profiles and journeys, see who they talk with, read segments, tags and admin notes. Never email addresses
tickets:readSupport ticketsList tickets: status, who opened and claimed them, and the subject. No tool returns a transcript
engagement:readPolls, giveaways and eventsList polls, giveaways, forms, suggestions, calendar events and campaigns, with their status
automations:readAutomationsList automations, their triggers and whether they are running
reports:readReportsList saved reports
knowledge:readKnowledge baseSearch the knowledge base and answered questions
signals:writeUpdate signalsAcknowledge, resolve or dismiss signals
members:writeTag members and write notesAdd or remove member tags and add admin notes
messages:writeDraft messagesSave messages as drafts in Scheduled Messages
engagement:writeDraft polls and giveaways, add eventsSave polls and giveaways as drafts, and add events to the calendar

Tools

Every tool starts with ceejay_. Several arguments are shared between tools:

  • server (every tool except ceejay_list_servers and ceejay_describe_metrics): the community, by name or Discord server ID. Leave it out to use the first community the connection was given. ceejay_list_servers lists them.
  • range (ceejay_get_pulse and the analytics tools): 1d (24 hours, the default), 1w, 1m, 3m, 6m, 1y or all. 24h, 7d, 30d and 90d are accepted as well. custom needs start and end as YYYY-MM-DD.
  • limit (tools that list things): how many rows to return. The maximum and default are in the tables.

Figures come with their change against the previous period. Text written by members, such as ticket subjects or example messages, is labelled as untrusted data in the result.

Reading

ToolPermissionWhat it returnsOther arguments
ceejay_list_serversnoneThe communities this connection can reach, which one is the default, and what agents may do in each
ceejay_get_pulsecommunity:readThe headline numbers with their change, and the signals asking for attention. The place to startrange, start, end
ceejay_list_signalscommunity:readSignals CEEJAY has raised, by severityseverity (critical, warning, info, all), status (active by default, acknowledged, resolved, dismissed), limit (1 to 50, default 20)
ceejay_describe_metricscommunity:readWhat CEEJAY means by a term on the dashboardterm; leave out for the whole glossary
ceejay_get_engagementanalytics:readMessages, active users, the share of members who posted, and who and whererange, start, end, channels, roles (comma-separated Discord IDs)
ceejay_get_member_growthanalytics:readJoins, leaves and net change, the biggest spikes and where joins came fromrange, start, end
ceejay_get_retentionanalytics:readWhether new members settle in and stay: roles, doing anything, posting and voice in their first week, still here after 30 days, leaving on day one, being active in weeks one and four, and weekly cohortsrange, start, end
ceejay_get_activityanalytics:readGames, music and tools members use, and time in voicerange, start, end, category (all, games, music, development)
ceejay_get_activity_timinganalytics:readThe busiest days and hours for messagesrange, start, end, tz (an IANA zone such as Europe/London, UTC by default)
ceejay_get_channel_analyticsanalytics:readMessages and unique members per channel. Channels excluded from analytics do not appearrange, start, end
ceejay_get_community_healthanalytics:readDaily, weekly and monthly active members, lifecycle, conversations and regular posters whose activity has halvedrange, start, end
ceejay_get_voice_of_communityanalytics:readThe health score and what drives it, sentiment, topics and example messagesrange, start, end
ceejay_get_keywordsanalytics:readMentions of the terms the community tracks, against the previous period, with sentimentrange, start, end
ceejay_get_invitesanalytics:readWho brings members in, how many stayed, and which sources joins came fromrange, start, end
ceejay_search_membersmembers:readMembers by name, segment or what they did in the community's apps. Returns names, join date, last activity, messages, XP, segment and tags. Never email addressesquery, segment (from ceejay_list_segments), activity, sortBy, sortOrder, page, limit (1 to 100, default 50)
ceejay_get_membermembers:readOne member in full: activity, XP and level, roles, tags, segments, admin notes and recent activity in the appsuserId (required)
ceejay_list_segmentsmembers:readThe audience segments, how many members are in each and how that changed over 7 days
ceejay_get_connectionsmembers:readHow members and topics connect, from the knowledge graph over the last 90 daysview (required: member, topic, circles, connectors, topic_links, knowledge_gaps), member, topic, limit (1 to 50, default 10)
ceejay_list_ticketstickets:readTickets with status, who opened and claimed them, and the subjectstatus (open by default, claimed, pending, resolved, closed, all), limit (1 to 200, default 50)
ceejay_list_automationsautomations:readEach automation's trigger, what it does, whether it is on and when it last ran
ceejay_list_engagement_itemsengagement:readPolls, giveaways, forms, suggestions and upcoming events, with their statuskind (all, polls, giveaways, forms, suggestions, events), limit (1 to 100, default 25)
ceejay_list_campaignsengagement:readThe community's campaigns: status, dates, goal, items and members reachedstatus (all, planned, live, ended, archived), limit (1 to 200, default 50)
ceejay_get_campaignengagement:readOne campaign with its results, as totalscampaignId (required)
ceejay_list_reportsreports:readSaved reports and whether each is a draft, published or scheduled. Archived reports are left out
ceejay_search_knowledgeknowledge:readKnowledge base documents and answered questions that matchquestion (required)

activity on ceejay_search_members takes an object with a kind. { "kind": "did", "app": "polls", "days": 30 } finds members who did something in an app: action narrows it to one action, days (1 to 365) limits it to that many days back and is all time when left out, and min (1 to 1,000, default 1) asks for at least that many times. { "kind": "campaign", "campaignId": 7 } finds members who took part in a campaign. { "kind": "none", "days": 30 } finds members who did nothing in any app for that many days. The apps and their actions are listed in the tool's own description.

The campaign tools say so when campaigns are not switched on for the community.

Changing

These tools only appear when the community has switched on Let agents make dashboard changes and drafts and ticked the permission.

ToolPermissionWhat it does
ceejay_update_signalsignals:writeSets a signal to acknowledged, resolved or dismissed. Arguments signalId and status, both required
ceejay_tag_membermembers:writeAdds or removes tags on one member. The tags must already exist, and are given by name: userId, addTags, removeTags
ceejay_bulk_tag_membersmembers:writeThe same for up to 50 members at once: userIds, addTags, removeTags
ceejay_add_member_notemembers:writeAdds a line to the member's admin notes, such as [2026-10-03] (via AI agent) Asked about the Saturday tournament, under the notes already there: userId, note
ceejay_draft_messagemessages:writeSaves a message as a draft in Scheduled Messages: channelId, content (up to 2,000 characters), optional title (Drafted by an AI agent when left out)
ceejay_create_eventengagement:writeAdds an event to the community calendar: title, eventDate (ISO 8601), optional description. Events show as markers on analytics charts
ceejay_create_pollengagement:writeSaves a poll as a draft: title, options (2 to 10), channelId
ceejay_create_giveawayengagement:writeSaves a giveaway as a draft: title, prizeDescription, channelId, optional durationMinutes (5 to 43,200, and 1,440 when left out) and maxWinners (1 to 100, and 1 when left out)

Drafts and changes. Messages, polls and giveaways are saved as drafts: nothing is posted until a person opens the draft in the dashboard and sends or starts it. Signal updates, tags, notes and calendar events take effect as soon as the change is confirmed. A calendar event is a real event, not a draft. Everything a tool makes is attributed to the person the agent acts as.

Confirming. A change tool does nothing the first time. Called without confirm, it returns a preview of what it would do. With dry_run: true it returns the same preview and states that nothing was changed. Only confirm: true makes the change. The agent is meant to show you the preview and wait for your confirmation before it calls again.

There is no tool to run database queries or search message text, to post in Discord, to close tickets or switch automations on or off, or to delete members, segments, reports or other items.

Limits

  • Each connection can make 120 reading calls and 20 changing calls a minute, and never more than the community's Requests per minute. A connection is one token, or one sign-in.
  • Over the limit, the tool returns an error result instead of an HTTP error: Rate limit reached (120 calls a minute for this connection). Try again in 30 seconds.
  • ceejay_bulk_tag_members takes up to 50 members at once, and every tool's limit has a maximum, shown above.

Activity log

Every tool call is logged. Settings → MCP server → Activity shows the 20 most recent: the tool, the agent's name, whether it succeeded (ok), failed (error) or was refused (denied), how long it took, and the error code. A filter picks All calls, Succeeded, Failed or Refused, and a second list shows the most used tools over the last 7 days.

The log also stores the client version, the token or sign-in it used, the user it acted as, the length of the result, and the caller's IP address. Of the arguments it stores a summary: identifiers, counts and ranges. Free text, such as a search or a note, is stored only as its length.

Connections refused before any tool runs, such as a bad token, are not logged.

Errors

A connection that is refused for its token, its sign-in or the community's policy gets an HTTP error with a body of { "error": "…", "code": "…" }.

StatuscodeMeaning
401missing_tokenNo Authorization: Bearer header
401invalid_tokenThe token is not valid
401not_an_agent_tokenThe credential is neither an agent token nor an agent sign-in. An API key (ceejay_sk_…) or a dashboard session was sent by mistake. Create an agent token
401revoked, expiredThe token was revoked, or the token or sign-in has expired
401unboundThe token is not linked to a user. Revoke it and create a new one
401user_lost_accessThe person behind the token or sign-in no longer has access to the community
401grant_revoked, no_servers, oauth_unconfiguredA sign-in problem: the agent was disconnected, it was given no community, or sign-in is not switched on
403agents_disabledAllow AI apps is off in every community the connection was given

Once connected, a tool that cannot run returns an error result: isError is true and the text says what to do. These are the codes you will see in the Activity tab:

CodeMeaning
agents_disabledAgents are off in the community the call names
writes_disabledThe community allows agents to read but not to change
policy_forbidsThe community does not allow that permission
insufficient_scopeThis token or sign-in was not given that permission
server_not_in_credentialThe server argument is not one of the connection's communities, or matches more than one
rate_limitedOver the per-minute limit
invalid_inputAn argument is missing or wrong
access_denied, not_found, upstream_error, rejectedThe request was denied, the item was not found, the upstream call failed or the request was rejected
activity_unavailableceejay_search_members was asked to filter by what members did in the apps, and that cannot be done here: the member history is not switched on, or the campaign is not one of the community's
tool_failed, request_failed, unreachable, not_configured, unhandledThe tool failed for a reason on CEEJAY's side. The text of the result says what happened

On this page

Community StudiosPrivacyTerms