How I Stopped Copy-Pasting Jira Tickets Into Codex

I got tired of being a human API between Jira and Codex.

That was the whole problem.

Every time I wanted Codex to work on a real ticket, I had to open Jira, copy the title, copy the description, copy the acceptance criteria, sometimes copy the recent comments, then paste all of that into the prompt before I could even ask it to start.

It was stupid work.

Not hard work. Not valuable work. Just stupid work.

And worse, it is exactly the kind of work that makes AI tooling feel more magical in demos than it does in real life. In a demo, people say, “Just ask the agent to do the task.” In practice, somebody still has to shovel the context into the prompt first.

I did not want that somebody to keep being me.

What I wanted was this:

Work on FM-1234.

That is what this project was about.

I built a Jira MCP server in Go so Codex could fetch ticket details directly from Jira. Now I can hand it a ticket key instead of pasting a small essay into every prompt.

The Problem With Copy-Paste Prompts

Copy-pasting Jira into prompts is annoying, but the real problem is that it is a bad interface.

It is slow.

It is easy to get incomplete.

It goes stale immediately.

And it trains you to think that “prompt engineering” means manually flattening your tooling into text.

Real Jira tickets are rarely just a title and a neat little description. They usually include:

  • acceptance criteria
  • linked metadata
  • clarifying comments
  • updates from product or engineering

So every time I pasted one into Codex, I was making two things worse at the same time:

  1. my workflow got slower
  2. the prompt got dirtier

Instead of writing a clean instruction like this:

Use jira_get_issue for FM-1234, summarize the requirements, implement the change, and run tests.

I was writing prompts bloated with context that should never have been in the prompt in the first place.

That was the motivation for the project. I was not trying to build “AI infrastructure” for the sake of it. I was trying to remove a useless step from a workflow I was already using every day.

Why MCP Was The Right Fit

I think a lot of people over-explain MCP. The practical value is simple: it gives the agent a structured way to use external systems instead of forcing you to paste everything as text.

In my case, the shape was obvious:

Codex -> MCP server -> Jira API

That is better than copy-paste prompts for a few reasons:

  • Codex can fetch the latest ticket details on demand
  • the workflow is reusable instead of being rebuilt every time
  • the data can be exposed as tools, not just dumped as prose
  • I can keep the same integration locally over stdio and remotely over HTTP

Once I saw that clearly, the project stopped feeling like “AI stuff” and started feeling like what it actually was: a small integration service with very normal engineering constraints.

What I Built

I built a read-oriented Jira MCP server in Go.

The scope was intentionally narrow. I did not start with “let the agent manage Jira.” I started with “stop making me act like a clipboard.”

The server exposes a small set of tools:

  • jira_get_issue
  • jira_search_issues
  • jira_my_open_issues

It also exposes issue content as a resource so clients can consume a normalized version of a ticket instead of raw Jira noise.

I kept it read-only on purpose.

I am opinionated about this: read access gives you most of the value with a fraction of the risk.

The biggest win here is not letting an agent transition issues or spray comments into Jira. The biggest win is letting it read the task from the source of truth without me manually relaying it.

The Real Target Workflow

The workflow I wanted was very simple:

  1. I mention a Jira key in a prompt.
  2. Codex uses the Jira MCP server to fetch the issue.
  3. It summarizes the work, inspects the codebase, and implements the task.
  4. I review the result instead of manually relaying ticket context.

That changes the shape of the whole interaction.

Before:

Here is the Jira ticket:

Title: ...
Description: ...
Acceptance criteria: ...
Comments: ...

Now inspect this repo and implement the change.

After:

Use the Jira MCP server to fetch FM-1234 first.
Summarize the ticket, inspect the repo, implement the change, and run tests.

That is a much better interface.

The prompt goes back to being an instruction instead of a data dump. That sounds like a small difference, but it changes the feel of the workflow completely.

Why I Chose Go

Go was the obvious choice for this kind of tool.

  • it is a good fit for small services
  • shipping a single binary is convenient
  • the standard library is enough for most of the plumbing
  • it encourages boring code, which is exactly what I wanted

This was not a project where cleverness would help. I wanted something small, legible, and deployable without ceremony.

The First Version: Stdio

I started with stdio because that is the fastest path to proving the idea works.

I only cared about three things at that stage:

  • can Codex call the tools
  • can the server authenticate to Jira
  • can the issue data be normalized into something useful for an agent

That first version mattered because it kept the project honest. Before I spent time on hosting, reverse proxies, or custom domains, I needed to know whether the workflow improvement was real.

It was.

The moment the local version worked, the value was obvious. I stopped copying Jira tickets into prompts and started giving Codex a ticket key instead.

That was enough to justify the rest of the work.

Normalizing Jira For An Agent

One of the most important parts of the build was not transport. It was shape.

Jira issue payloads are designed for Jira, not for coding agents.

If you hand raw Jira JSON to an agent, you get what you deserve:

  • nested field structures
  • formatting metadata
  • fields that are technically present but practically irrelevant

So I normalized the issue data into something more useful:

  • concise issue metadata
  • a readable text representation
  • structured fields the client can still use downstream

I think this part gets underrated. Access alone is not enough. Good tooling reduces entropy before the data ever reaches the model.

From Local Tool To Deployable Service

Once the stdio flow worked, I wanted a deployable HTTP version too.

The runtime now supports:

  • stdio for local use
  • streamable HTTP for remote access

I deployed it as an HTTP service, protected it with bearer token auth, added a custom domain, and configured Codex to talk to the remote endpoint.

At that point the architecture looked more like this:

Codex client
  -> remote MCP endpoint
  -> Jira API

That is a better setup if I want to reuse the server across machines, shells, or tools without treating my laptop as the integration layer.

One Gotcha: HTTP MCP Session Initialization

The most annoying protocol detail showed up after I deployed the HTTP version.

A plain curl to the MCP endpoint does not prove much. With stateful HTTP MCP, you need the right sequence:

  1. initialize
  2. capture Mcp-Session-Id
  3. send notifications/initialized
  4. then call methods like tools/list

If you skip that flow, you can get errors that look confusing at first, such as methods being “invalid during session initialization”.

That was a good reminder that once you move from local stdio to hosted HTTP, you are not just writing a wrapper anymore. You are dealing with protocol state, auth, sessions, hosting, and all the boring details that demos conveniently skip.

Making Deployment Boring

I wanted deployment to be boring, because boring is what makes internal tools survive.

The server now supports:

  • environment-based configuration
  • .env loading with sane precedence
  • Docker builds
  • Heroku-friendly HTTP binding via PORT
  • bearer token protection for the remote MCP endpoint

That stuff matters more than people like to admit. A lot of internal tools die in the gap between “it works locally” and “I trust it enough to depend on it.”

My standard for success was simple:

  • deploy it
  • hit /healthz
  • point Codex at the /mcp endpoint
  • stop thinking about the server unless something actually breaks

If the tool itself becomes noisy, then all I have done is replace one annoying workflow tax with another.

Observability Was Worth Adding Early

Once the service was remotely deployed, observability stopped being optional.

I added Sentry so I could capture:

  • runtime errors
  • request traces
  • mirrored application logs
  • server-side failures and 5xx paths

I am glad I added that early. Protocol bugs and deployment bugs are much less interesting when you are debugging them blind.

Without observability, the loop looks like this:

  • the client says something vague failed
  • you check the server
  • you reproduce manually
  • you guess

With observability, you get a far tighter loop.

For a service that sits between a coding agent and Jira, that is important. If the tool layer is unreliable, the user stops trusting the entire workflow.

CI Was Also Non-Negotiable

This repo is small, but small repos still deserve CI.

I added GitHub Actions for:

  • gofmt checks
  • go mod tidy verification
  • tests
  • build verification
  • Docker build validation

That is not over-engineering. It is the minimum needed to keep a small integration service honest.

I do not want to rediscover a broken auth path or a bad config regression in production because I was too lazy to add a basic workflow.

What Changed In My Day-To-Day Workflow

This is the only part that really matters.

Before, I had to manually translate Jira into prompt input.

Now, I can do something much closer to this:

Use Jira to fetch FM-1234, summarize the task, inspect the repo, implement the change, and run tests.

Or, with a standing instruction in my environment, simply:

Work on FM-1234.

That is the win.

The interface between me and Codex is smaller and cleaner:

  • I provide intent
  • the server provides ticket context
  • Codex works from the source of truth

That is how these tools should feel. I do not want to babysit the context-loading step if the information already exists in a system the agent can read.

What I Would Add Next

If I keep pushing the project forward, the next improvements are pretty obvious.

The first is deeper trace instrumentation around outbound Jira calls so latency inside the Jira client is easier to understand.

The second is better ticket context shaping, especially around comments and acceptance criteria, so the most relevant parts rise to the top more aggressively.

The third is carefully scoped write operations, such as adding comments or transitioning issues, but only if I am confident the safety model is clear.

I would still keep the default posture conservative. Read access is where most of the value is, and it is much easier to trust than write automation.

Lessons From Building It

Building this reinforced a few opinions I already had.

1. Practical AI tooling starts with workflow pain

The project existed because copy-pasting Jira tickets into prompts was annoying enough to be worth fixing.

That is a better starting point than trying to invent a use case for a protocol or an agent.

2. Transport is less interesting than interface quality

MCP is useful, but the protocol is not the headline.

The headline is that Codex can ask for the right ticket data in a clean, structured way without me manually stuffing it into the prompt.

3. Deployment and observability matter quickly

The moment a tool becomes part of your daily workflow, you need enough operational maturity that it can be trusted.

That means:

  • sane config
  • clear auth
  • a health check
  • CI
  • traces and error visibility

4. Read-only tools can be incredibly useful

You do not need full autonomy to get real value. Sometimes the biggest win is simply giving the agent direct access to the right context at the right time.

Final Thoughts

I built this because I was tired of copy-pasting Jira into Codex.

That is not a glamorous origin story, but I think it is the right kind.

The best internal tools usually start with a very ordinary frustration. Then, if you solve the right problem cleanly, they become part of your default workflow.

That is what happened here.

I no longer think in terms of “paste the ticket into the prompt.” I think in terms of “give Codex the ticket key and let the tooling do its job.”

To me, that is the real value of projects like this. Not that they sound futuristic. Not that they use a new protocol. The value is that they reduce friction in a workflow you already care about.

In this case, the difference between:

Here is a pasted Jira ticket. Please work on it.

and:

Work on FM-1234.

is bigger than it looks.