This repository provides two integrations with Fluxer:
| Integration | Project | Use case |
|---|---|---|
| REST API Client | JJConsulting.Fluxer.Client |
Call Fluxer's identity and Admin REST APIs directly from your .NET application. Can be used independently of the MCP server. |
| MCP Server | JJConsulting.Fluxer.Mcp |
Connect AI assistants such as ChatGPT to Fluxer through MCP tools. Uses the REST client internally to read the signed-in user's non-NSFW individual DMs. |
Use the REST client to integrate Fluxer into your application, or run the MCP server to expose Fluxer tools to an AI assistant. Setup instructions for each integration follow below.
dotnet add package JJConsulting.Fluxer.Client --version 0.1.0Typed .NET client for Fluxer's identity and Admin APIs, with dependency injection support.
{
"Fluxer": {
"ApiBaseUrl": "https://your-fluxer-instance.com/api",
"AdminApiKey": "YOUR_ADMIN_API_KEY"
}
}Register the clients and retrieve the signed-in user's DMs:
using JJConsulting.Fluxer.Client;
using JJConsulting.Fluxer.Client.Configuration;
using JJConsulting.Fluxer.Client.Models;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
var builder = Host.CreateApplicationBuilder(args);
builder.Services.AddFluxerClient(builder.Configuration.GetSection("Fluxer"));
using var host = builder.Build();
var identityClient = host.Services.GetRequiredService<FluxerIdentityClient>();
var identityResult = await identityClient.GetIdentityAsync("USER_OAUTH_ACCESS_TOKEN");
if (identityResult is FluxerError identityError)
{
Console.WriteLine($"Identity failed: {identityError.Failure}");
return;
}
if (identityResult is not FluxerIdentity identity)
return;
var adminClient = host.Services.GetRequiredService<FluxerAdminClient>();
var dmResult = await adminClient.GetIndividualDmPageAsync(identity.Id);
if (dmResult is FluxerError dmError)
{
Console.WriteLine($"DM lookup failed: {dmError.Failure}");
return;
}
if (dmResult is FluxerDmPage page)
foreach (var channel in page.Channels)
Console.WriteLine(channel.ChannelId);ApiBaseUrl must use HTTPS and omit /v1. Keep the Admin API key on the server; your application must authorize access to each user and channel. FluxerAdminClient also provides GetMessagesAsync, SearchMessagesAsync, GetUserGuildPageAsync and GetGuildAsync. Guild queries return typed models through the same transport and error handling as DM queries. FluxerAdminClient.GetReadableChannelsAsync(guildId, userId, accessToken) computes readable channels and history cutoffs using the user's guilds OAuth scope and the Admin key's guild:list:members ACL. The caller must bind userId to the authenticated token owner.
flowchart LR
user["User"] -->|Sign in and authorize| oauth["Fluxer OAuth 2.0"]
oauth -->|Authentication callback| mcp["JJConsulting.Fluxer.Mcp"]
mcp -->|Authorized requests| api["Fluxer REST API"]
Fluxer OAuth identifies the signed-in user. The MCP server uses that identity to authorize access to conversations and calls the Fluxer API with server-side credentials.
Release tags (v*) publish the Docker image to ghcr.io/jjconsulting/fluxer-mcp:<version> (without the v prefix).
Requires a dedicated Fluxer OAuth application and an Admin API key. The key and its owner need user:list:dm_channels and message:lookup; the owner also needs admin:authenticate.
Prerequisites: Git, Docker Engine with the Compose plugin (docker compose), and a public hostname with an HTTPS reverse proxy on the server. The Docker build includes the .NET SDK; you do not need to install .NET on the host.
-
Clone the repository and create the environment file:
git clone https://github.com/JJConsulting/JJConsulting.Fluxer.git cd JJConsulting.Fluxer cp .env.example .env chmod 600 .env -
Edit
.envwith your instance and credentials:ASPNETCORE_ENVIRONMENT=Production AllowedHosts=localhost;127.0.0.1;[::1];mcp.example.com Fluxer__ApiBaseUrl=https://your-fluxer-instance.com/api Fluxer__ClientId=YOUR_CLIENT_ID Fluxer__ClientSecret=YOUR_CLIENT_SECRET Fluxer__AdminApiKey=YOUR_ADMIN_API_KEY Fluxer__PublicUrl=https://mcp.example.com Fluxer__AllowedClientRedirectUris__0=https://chatgpt.com/connector_platform_oauth_redirect
Replace
mcp.example.comwith your public hostname.Fluxer__ApiBaseUrlmust use HTTPS and omit/v1and the trailing slash.Fluxer__PublicUrlmust be the public HTTPS origin, without a path or trailing slash. Use the exact ChatGPT callback forFluxer__AllowedClientRedirectUris__0(see Connect ChatGPT below). Keep.envprivate and out of Git. -
In your Fluxer OAuth application, register
https://mcp.example.com/auth/fluxer/callbackas a redirect URI. -
To use the published image, replace the contents of
compose.yamlwith the example below. Replace<version>with a published release version, without thevprefix:services: fluxer-mcp: image: ghcr.io/jjconsulting/fluxer-mcp:<version> restart: unless-stopped ports: - "127.0.0.1:8080:8080" env_file: .env
Start the MCP server:
docker compose pull docker compose up -d docker compose ps docker compose logs --tail=100 fluxer-mcp curl --fail http://127.0.0.1:8080/health
To build from source instead, keep the repository's original
compose.yamland rundocker compose up -d --buildinstead of the pull and start commands above.Both configurations publish HTTP port
8080only on the host's loopback interface and restart the container unless explicitly stopped. Run one instance because pending OAuth logins are stored in memory. -
Configure Nginx or Caddy below, then verify the public endpoints.
Both examples assume the proxy runs on the host. Point
mcp.example.comto your server and allow inbound ports80and443.Add this to your existing HTTPS server block for
mcp.example.com, with its TLS certificate configured:location / { proxy_pass http://127.0.0.1:8080; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header Connection ""; proxy_buffering off; proxy_read_timeout 3600s; }
sudo nginx -t && sudo systemctl reload nginxAdd to
/etc/caddy/Caddyfile:mcp.example.com { reverse_proxy 127.0.0.1:8080 { flush_interval -1 } }
Caddy manages HTTPS certificates automatically. See the Caddy reverse proxy documentation.
sudo caddy validate --config /etc/caddy/Caddyfile sudo systemctl reload caddy
If the proxy runs in Docker, use a shared network and upstream
fluxer-mcp:8080instead of127.0.0.1:8080. -
Verify the public endpoints, then follow Connect ChatGPT below using
https://mcp.example.com:curl --fail https://mcp.example.com/health curl --fail https://mcp.example.com/.well-known/oauth-authorization-server
Both endpoints should return HTTP
200, and the OAuth metadata'sissuershould equalFluxer__PublicUrl.
After editing .env, run docker compose up -d to apply changes. To update the published image, change its version in compose.yaml, then run docker compose pull followed by docker compose up -d. For source builds, run git pull followed by docker compose up -d --build. To stop it, run docker compose down. Restarts discard pending OAuth logins; retry any sign-in that was in progress.
Requires .NET SDK 11.0.100.
Create src/JJConsulting.Fluxer.Mcp/appsettings.Development.json (git-ignored):
{
"AllowedHosts": "localhost;127.0.0.1;[::1];YOUR_PUBLIC_HOST",
"Fluxer": {
"ApiBaseUrl": "https://your-fluxer-instance.com/api",
"ClientId": "YOUR_CLIENT_ID",
"ClientSecret": "YOUR_CLIENT_SECRET",
"AdminApiKey": "YOUR_ADMIN_API_KEY",
"PublicUrl": "https://YOUR_PUBLIC_HOST",
"AllowedClientRedirectUris": ["https://chatgpt.com/connector_platform_oauth_redirect"]
}
}ApiBaseUrl must use HTTPS and omit /v1 and the trailing slash. PublicUrl is the public HTTPS origin, without a path or trailing slash. Keep credentials on the server and out of Git.
AllowedClientRedirectUris accepts multiple exact callback URLs, one per client. For environment variables, use Fluxer__AllowedClientRedirectUris__0, Fluxer__AllowedClientRedirectUris__1, etc. Migration: replace the former ClientRedirectUri string with this array; the old setting is no longer used.
dotnet dev-certs https --trust
dotnet run --project src/JJConsulting.Fluxer.Mcp --launch-profile httpsFor local development with ChatGPT, expose the server in another terminal:
ngrok http https://localhost:7042Use the tunnel hostname in AllowedHosts and its HTTPS origin in PublicUrl. Restart the server after configuration changes. Keep both processes running.
-
In the Fluxer OAuth application, register
https://YOUR_PUBLIC_HOST/auth/fluxer/callback. -
Create a custom MCP app in ChatGPT (enable developer mode if required):
Setting Value MCP server URL https://YOUR_PUBLIC_HOSTAuthentication OAuth Client mode Pre-defined Client ID Same as Fluxer:ClientIdClient Secret Empty Token endpoint authentication None Registration URL Empty Scope identify -
Add the exact callback displayed by ChatGPT to
Fluxer:AllowedClientRedirectUrisand restart the server if it changes. This differs from the Fluxer callback in step 1. -
Connect, sign in to Fluxer, and select the app in a conversation.
If the tunnel hostname changes, update AllowedHosts, PublicUrl, the Fluxer callback, and the MCP URL in ChatGPT. Refresh ChatGPT's tool list after server tool changes.
Connection failing? Check that public /health and /.well-known/oauth-authorization-server return HTTP 200, and that the metadata's issuer matches PublicUrl.
| Tool | Purpose |
|---|---|
get_profile |
Get your Fluxer user ID. |
list_my_dm_channels |
Page through your DMs. |
get_my_recent_conversations |
Get recently active DMs and message previews. |
find_my_dm_contacts |
Find a DM by contact name, username, or ID. |
get_my_dm_messages |
Read a DM, optionally filtering by date. |
get_my_dm_activity |
Read messages across your DMs for a period. |
search_my_dm_messages |
Search text in one DM. |
list_my_guilds |
List guilds you belong to; continue with NextAfter. |
list_my_guild_channels |
List non-NSFW text channels whose messages you can read. |
get_my_guild_messages |
Read a guild channel; follow NextBefore while HasMore. |
- Guild tools require Admin ACLs
user:list:guilds,guild:lookup, and (for non-owners)guild:list:memberson the key and its owner. Membership uses the authenticated user ID. Non-owners must reconnect to grant the OAuthguildsscope: channel visibility comes fromGET /guilds/{id}using their bearer token. History access is computed from their roles and channel overwrites; the guild's history cutoff applies withoutREAD_MESSAGE_HISTORY. Messages from all authors are returned within these permissions. NSFW guilds and channels are excluded. Filtered pages may be empty; continue withNextBeforewhileHasMoreis true. - Dates require ISO-8601 timestamps with a UTC offset. For a day in São Paulo:
start: "2026-11-11T00:00:00-03:00",end: "2026-11-12T00:00:00-03:00". Start is inclusive; end is exclusive. - Activity returns up to 100 messages per channel. Continue with
NextChannelBeforefor more channels; useget_my_dm_messageswith the same dates and each conversation'sNextBeforewhileHasMoreis true. - Search returns at most 100 matches (
Truncatedindicates more). Empty results may mean the instance has no search index. - Run one MCP process: pending OAuth logins are stored in memory and lost on restart.
Fluxer is an independent project and is not developed or owned by JJ Consulting. This repository provides third-party integrations and is not affiliated with or endorsed by Fluxer.