Jira Thing
About
An example MCP server for interacting with Jira, deployable on Cloudflare Workers.
Details
- Author
- portnumber53
- Categories
- Productivity, Project Management, Other
Jump to
3 Configure Slack Request Urls Worker Endpoints You Ll Add
Once implemented, Slack should point to Worker endpoints like:
- Slash command request URL:.../slack/commands
- Interactivity request URL:.../slack/interactions
- Events request URL(if enabled):.../slack/events
- Verify Slack signaturesusingSLACK_SIGNING_SECRET
- Ack within 3 seconds(useresponse_urlor async follow-ups for slow Jira calls)
Model Context Protocol (MCP) Server with GitHub OAuth
This project provides a template for aModel Context Protocol (MCP)server that uses GitHub for OAuth 2.0 authentication. It is built to run onCloudflare Workers, providing a robust and scalable foundation for your own remote MCP services.
- GitHub OAuth Integration: Securely authenticates users via GitHub, acting as an OAuth client to GitHub and an OAuth server to the MCP client.
- Dynamic Tool Loading: Demonstrates how to conditionally expose tools based on the authenticated user's identity.
- Example Tools: Includes two sample tools:
- A publicaddtool available to all authenticated users.
- A restrictedgenerateImagetool that is only available to a predefined list of authorized users.
- Node.js(v18 or later)
- ACloudflare account
- npmor a compatible package manager
git clone https://github.com/PortNumber53/mcp-jira-thing.git cd mcp-jira-thing
A React + Vite single-page application lives underfrontend/. It provides the GitHub sign-in flow for users and can be developed with the standard Vite server.
cd mcp-jira-thing npm install # install Worker + shared dependencies (repo root) cd frontend npm install # install local dependencies before development npm run dev # starts the Vite development server with HMR npm run dev:worker # runs the merged Worker locally on :18112
npm run build # runs tsc + vite build (Worker deploy is from repo root) npm run deploy # uploads the merged Worker and SPA assets via wrangler deploy
The Cloudflare Worker is defined at the repository root (src/index.ts). It serves the SPA fromfrontend/dist/clientat/and exposes the MCP server under/sse(and/mcp). Deployment is performed from the repo root withnpm run deploy, which builds the SPA first. Runningnpm run dev:workerinfrontend/starts the same merged Worker locally using../wrangler.jsonc.
Slack App (per-channel Jira project integration)
If you want Slack users to interact with Jirain the context of a Slack channel(e.g. create/search issues against a default Jira project per channel), connect a Slack app to this Worker and store a mapping:
- Slack channel ID→Jira project key(e.g.C01234567→ENG)
This repository doesnotship Slack endpoints yet, but the steps below describe the configuration you’ll need once you add them.
In Slack, create an app from scratch and enable:
- Interactivity & Shortcuts(optional but recommended)
- Slash Commands(recommended)
- Event Subscriptions(optional; useful for@yourappmentions)
- OAuth & Permissions
Add a redirect URL for the Worker, for example:
- http://localhost:18112/slack/oauth/callback(local)
- https://<your-worker>.<your-subdomain>.workers.dev/slack/oauth/callback(prod)
Suggested bot token scopes (adjust to your needs):
- commands: enable slash commands like/jira
- chat:write: post responses/messages
- channels:readand/orgroups:read: read channel metadata (public vs private)
- app_mentions:read(if using events for mentions)
- users:read(if you want to display usernames / enrich messages)
3) Configure Slack request URLs (Worker endpoints you’ll add)
Once implemented, Slack should point to Worker endpoints like:
- Slash command request URL:.../slack/commands
- Interactivity request URL:.../slack/interactions
- Events request URL(if enabled):.../slack/events
- Verify Slack signaturesusingSLACK_SIGNING_SECRET
- Ack within 3 seconds(useresponse_urlor async follow-ups for slow Jira calls)
4) Store “channel → Jira project” mapping
- Store mapping in Cloudflare KV(simple) or aDurable Object(strong consistency)
- Key by channel ID, value includes at least{ projectKey, updatedAt, updatedBy }
- slack:channel-project:C01234567→{"projectKey":"ENG","updatedAt":...}
One practical pattern is a/jiracommand that sets or uses the channel’s default project:
- Set default:/jira project set ENG
- Show default:/jira project get
- Create issue:/jira create "Bug title" --type Task
- Search:/jira search status=Open assignee=me
Implementation detail: the handler reads the channel ID from Slack payload, looks up the project key for that channel, then calls the existing Jira client/tooling insrc/tools/jira/to perform the requested action.
Add these as Wrangler secrets/vars (names are suggestions; pick a convention and stick to it):
- SLACK_SIGNING_SECRET: required to verify Slack requests
- SLACK_CLIENT_ID/SLACK_CLIENT_SECRET: required for Slack OAuth install flow
- SLACK_BOT_TOKEN: required to call Slack APIs (or store per-workspace tokens after OAuth)
If you support multiple Slack workspaces, store the workspace/team install info keyed byteam_id, not globally.
The Go backend exposes REST endpoints that serve data to the frontend (or other consumers). The initial implementation ships with:
- GET /healthz— simple health probe for load balancers and Jenkins smoke checks.
- GET /api/users?limit=50— returns a paginated list of NextAuth users from the database.
Create a copy ofbackend/env.exampleand provide the required values:
go testand the runtime code expect the environment variables to be present. When running locally you can export them or use a dotenv loader (direnv,dotenvx, etc.).
cd backend cp env.example .env # edit with your credentials (or export env vars) go test ./... go run ./cmd/server # or via make make test make run
Airoffers live-reload for Go applications so changes rebuild and restart automatically during development, shrinking feedback loops1.
# install once (requires Go 1.25+) go install github.com/air-verse/air@latest # start the watcher from the backend directory cd backend make dev # runs air -c .air.toml`
Air uses the configuration atbackend/.air.tomlto rebuild./tmp/mainwhenever Go or environment files change, then restarts the server transparently. Ensure your.envvalues are present before launching the watcher.
- Build/Test:make buildcompiles a Linux static binary atbackend/bin/mcp-backend.make testruns the unit tests. Both commands are orchestrated by the Jenkins pipeline (see below).backend/bin/mcp-backend.tar.gz
- Artifact:Jenkins compresses the binary toand publishes it as a build artifact.scripts/deploy-backend.sh
- Deploy script:cross-compiles the Linux binary, uploads it viascp, unpacks it under$DEPLOY_PATH, and optionally restarts a systemd service whenSERVICE_NAMEis provided.
This repository now contains a top-levelJenkinsfilethat performs the following stages:go test ./...
- Checkout— pulls the repository for the current build.
- Go Test— runsinsidebackend/.make build
- Build Backend— executesto generate themcp-backendbinary.scripts/deploy-backend.sh
- Archive Artifact— tars the binary and archives it for later retrieval.
- Deploy (master only)— executes, which expects the following environment variables to be supplied by Jenkins credentials or job configuration:
- DEPLOY_HOST: Production server host/IP (Arch Linux).DEPLOY_USER
- : SSH user with permission to write intoDEPLOY_PATHandrunsudo systemctl restarton the target service.DEPLOY_PATH
- : Target directory on the server (e.g./opt/mcp-backend).SERVICE_NAME
- (optional): systemd unit name to restart after deployment.
The deploy stage only runs for builds on themasterbranch, so feature branches remain test-only. Jenkins must provide SSH access, typically via an SSH key credential associated with theDEPLOY_USERaccount. Reviewscripts/deploy-backend.shfor additional details or customization points.
Follow these steps to configure and deploy your MCP server.
First, you need to create aGitHub OAuth Appto get your client credentials.
- Homepage URL:https://<your-worker-name>.<your-subdomain>.workers.devhttps://<your-worker-name>.<your-subdomain>.workers.dev/callback/github
- Authorization callback URL:
Once the app is created, note theClient IDand generate a newClient secret.
Next, use Wrangler to securely store your GitHub credentials and a session encryption key as secrets.
For theSESSION_SECRET, you can generate a secure random string withopenssl rand -hex 32.
To grant access to restricted tools likegenerateImage, you must add the GitHub usernames of authorized users to theALLOWED_USERNAMESset insrc/index.ts.
// src/index.ts const ALLOWED_USERNAMES = new Set<string>([ "PortNumber53", // Add other authorized GitHub usernames here ]);
Finally, deploy your configured worker to Cloudflare.
This MCP server exposes the following tools:
- Description: Adds two numbers.
- Access: Public (available to all authenticated users).
- Parameters:a(number),b(number).
- Description: Generates an image using the@cf/black-forest-labs/flux-1-schnellmodel.ALLOWED_USERNAMES
- Access: Restricted (only available to users in).prompt
- Parameters:(string),steps(number, 4-8).
You can test your remote server using theMCP Inspector:
npx @modelcontextprotocol/inspector@latest
Enter your worker's SSE URL (https://<your-worker-name>.<your-subdomain>.workers.dev/sse) and clickConnect. You will be redirected to GitHub to authenticate. Once authenticated, you will see the available tools in the Inspector.
- src/index.ts: The main entry point for the Cloudflare Worker. Defines the MCP server, its tools, and the logic for conditional tool access.src/github-handler.ts
- : Contains the logic for handling the GitHub OAuth flow.src/workers-oauth-utils.ts
- : Provides utility functions for the OAuth process, adapted from theworkers-oauth-providerlibrary.wrangler.jsonc
- : The configuration file for the Cloudflare Worker.package.json
- : Defines project scripts and dependencies.
You now have a remote MCP server deployed!
This MCP server uses GitHub OAuth for authentication. All authenticated GitHub users can access basic tools like "add" and "userInfoOctokit".
The "generateImage" tool is restricted to specific GitHub users listed in theALLOWED_USERNAMESconfiguration:
// Add GitHub usernames for image generation access const ALLOWED_USERNAMES = new Set(["yourusername", "teammate1"]);
Access the remote MCP server from Claude Desktop
Open Claude Desktop and navigate to Settings -> Developer -> Edit Config. This opens the configuration file that controls which MCP servers Claude can access.
Replace the content with the following configuration. Once you restart Claude Desktop, a browser window will open showing your OAuth login page. Complete the authentication flow to grant Claude access to your MCP server. After you grant access, the tools will become available for you to use.
{ "mcpServers": { "math": { "command": "npx", "args": [ "mcp-remote", "https://mcp-github-oauth.<your-subdomain>.workers.dev/sse" ] } } }
Once the Tools (under 🔨) show up in the interface, you can ask Claude to use them. For example: "Could you use the math tool to add 23 and 19?". Claude should invoke the tool and show the result generated by the MCP server.
If you'd like to iterate and test your MCP server, you can do so in local development. This will require you to create another OAuth App on GitHub:
- For the Homepage URL, specifyhttp://localhost:18112http://localhost:18112/callback/github
- For the Authorization callback URL, specify.dev.vars
- Note your Client ID and generate a Client secret.
- Create afile in your project root with:
GITHUB_CLIENT_ID=your_development_github_client_id GITHUB_CLIENT_SECRET=your_development_github_client_secret
Run the server locally to make it available athttp://localhost:18112`wrangler dev
To test the local server, enterhttp://localhost:18112/sseinto Inspector and hit connect. Once you follow the prompts, you'll be able to "List Tools".
When using Claude to connect to your remote MCP server, you may see some error messages. This is because Claude Desktop doesn't yet support remote MCP servers, so it sometimes gets confused. To verify whether the MCP server is connected, hover over the 🔨 icon in the bottom right corner of Claude's interface. You should see your tools available there.
To connect Cursor with your MCP server, chooseType: "Command" and in theCommandfield, combine the command and args fields into one (e.g.npx mcp-remote https://<your-worker-name>.<your-subdomain>.workers.dev/sse).
Note that while Cursor supports HTTP+SSE servers, it doesn't support authentication, so you still need to usemcp-remote(and to use a STDIO server, not an HTTP one).
You can connect your MCP server to other MCP clients like Windsurf by opening the client's configuration file, adding the same JSON that was used for the Claude setup, and restarting the MCP client.
Cloudflare Workers route/sseand/mcptraffic to the standalone Node.js MCP server configured byMCP_SERVER_URL. The Node service does not connect to the database directly. It persists transport-session identity, initialization metadata, tenant ownership, expiry, and last-seen timestamps through the Go backend's protected internal API. The Go backend is the only database owner and stores those records in PostgreSQL.
Sign in to leave a review
Use Google, GitHub, or an email account so ratings stay tied to real people.
No reviews posted yet.





