Turn Jira epics into stakeholder update decks automatically. Learn how to use MCP and Preso's API to generate on-brand presentations from tracked work.
You have a Jira epic. It's tracked meticulously: child issues, acceptance criteria, sprint assignments, status updates. Your team knows exactly where the work stands. But when it comes time to present that epic to stakeholders, executives, or the board, you start from scratch. You open PowerPoint. You stare at a blank slide. You copy fields from Jira, paste them into a deck, manually format everything to match your brand, and hope the narrative flows.
This is wasted motion. The data already exists. The structure is already there. The only missing piece is a bridge from your tracking system to a presentation that actually looks designed.
That bridge is MCP. The Model Context Protocol lets your agents and workflows connect to Jira, pull epic data, and feed it directly to Preso's API to generate a finished, on-brand deck in seconds. No copy-paste. No manual formatting. No generic template. Just a stakeholder update that reflects the real status of your work and matches your brand every single time.
This guide walks you through the entire workflow: setting up MCP, connecting to Jira, extracting epic data, and generating a presentation deck that's ready to present.
Before you start, make sure you have:
MCP is an open protocol created to let AI agents and tools talk to each other without building custom integrations for every pair. Instead of Preso needing a direct integration with Jira, and Jira needing an integration with Preso, both systems speak MCP. Your agent can use any MCP server, pull data from any tool, and pass it to any other tool that understands the protocol.
For deck generation, MCP is powerful because it lets you build a workflow like this:
This is not a one-off script. It is a repeatable workflow that scales. Once set up, you can generate stakeholder decks on demand, either manually or triggered by events (sprint completion, epic status change, scheduled board meetings).
Preso's MCP implementation lets you generate decks from your stack and works both ways: external agents can call Preso to create decks, and Preso's agents can reach out to any MCP-enabled tool to pull data. This is what MCP, the layer that connects your agents is all about.
To pull epic data from Jira programmatically, you need an API token. This token authenticates your requests to the Jira Cloud REST API without exposing your password.
In your Jira Cloud workspace:
You will also need your Jira instance URL (for example, https://yourcompany.atlassian.net) and your Jira email address. These three pieces of information (email, token, instance URL) are what you need to authenticate with the Jira REST API.
Before you write code to extract epic data, understand what information Jira stores about an epic and how to retrieve it.
An epic in Jira contains:
To retrieve this data, you will use the Jira Cloud platform REST API. The key endpoint is the issues endpoint with a JQL query to filter for your epic.
Here is an example curl request to fetch an epic and its child issues:
curl -u [email protected]:YOUR_API_TOKEN \
"https://yourcompany.atlassian.net/rest/api/3/search?jql=parent=PROJ-42"
This returns a JSON response with all the issues linked to the epic. Each issue includes fields like summary, status, assignee, and custom fields. You will parse this JSON and extract the fields you want to include in your presentation.
For a comprehensive guide to setting up Jira with MCP, see Workato's step-by-step guide to integrating Jira with MCP, which covers authentication and setup details in detail.
Now you need an MCP server that acts as a bridge between your agent and Jira. This server exposes Jira data as tools that your agent can call.
You have two options:
If you choose to build your own, here is a minimal example in Node.js using the MCP SDK:
const { Server } = require('@modelcontextprotocol/sdk/server/index.js');
const { StdioServerTransport } = require('@modelcontextprotocol/sdk/server/stdio.js');
const axios = require('axios');
const jiraUrl = process.env.JIRA_URL;
const jiraEmail = process.env.JIRA_EMAIL;
const jiraToken = process.env.JIRA_TOKEN;
const server = new Server({
name: 'jira-mcp',
version: '1.0.0',
});
server.tool('get_epic', {
description: 'Fetch an epic and its child issues from Jira',
inputSchema: {
type: 'object',
properties: {
epic_key: {
type: 'string',
description: 'The Jira epic key, e.g., PROJ-42',
},
},
required: ['epic_key'],
},
handler: async (args) => {
const epicKey = args.epic_key;
const jql = `parent=${epicKey}`;
const auth = Buffer.from(`${jiraEmail}:${jiraToken}`).toString('base64');
const response = await axios.get(
`${jiraUrl}/rest/api/3/search?jql=${jql}`,
{
headers: { Authorization: `Basic ${auth}` },
}
);
return response.data;
},
});
const transport = new StdioServerTransport();
server.connect(transport);This server exposes a single tool, get_epic, that your agent can call. When called with an epic key, it queries Jira and returns the epic data and all child issues.
Deploy this server where your agent can reach it (locally, on a server, or in a container). Your agent will communicate with it over stdio or HTTP, depending on your setup.
Once you have Jira data flowing, you need to tell Preso to generate a deck from it. Preso runs an MCP server that your agent can call to create presentations.
Contact Preso at https://www.trypreso.com/api to get your MCP server endpoint and credentials. Preso's MCP server exposes a tool like create_presentation that accepts:
Here is a pseudo-code example of how your agent would call it:
const epicData = await mcp.call('jira', 'get_epic', { epic_key: 'PROJ-42' });
const narrative = `
Epic: ${epicData.key} - ${epicData.fields.summary}
Status: ${epicData.fields.status.name}
Progress:
${epicData.issues.map(issue => `- ${issue.key}: ${issue.fields.summary} (${issue.fields.status.name})`).join('\n')}
Next steps: ${epicData.fields.customfield_nextSteps || 'TBD'}
`;
const deck = await mcp.call('preso', 'create_presentation', {
narrative: narrative,
brand_kit_id: 'your-brand-kit-id',
format: 'stakeholder_update',
output: 'powerpoint',
});
console.log('Deck generated:', deck.url);Preso's engine generates the deck instantly. Every slide is on-brand, fully editable, and ready to present. Learn more about headless presentations and how to generate decks from your stack.
Raw Jira data is not a story. You need to transform it into a narrative that makes sense for a stakeholder presentation.
Here is how to structure your narrative:
Slide 1: Title and Context Include the epic name, owner, and current status. Set the stage for why this epic matters.
Slide 2: Problem and Goal What problem does this epic solve? What is the goal? Pull this from the epic description.
Slide 3: Progress Show how many child issues are done, in progress, and to do. Use counts and percentages if possible. Preso can turn numbers into slides that land with automatically styled charts.
Slide 4: Key Milestones Highlight the major child issues or milestones. Show what is shipping next.
Slide 5: Blockers and Risks Are there any blockers? Any at-risk issues? Be transparent.
Slide 6: Next Steps and Timeline What is the next phase? When is the epic expected to close?
Here is a code example that shapes Jira data into this narrative:
function buildNarrative(epic, issues) {
const done = issues.filter(i => i.fields.status.name === 'Done').length;
const inProgress = issues.filter(i => i.fields.status.name === 'In Progress').length;
const todo = issues.filter(i => i.fields.status.name === 'To Do').length;
const total = issues.length;
const donePercent = Math.round((done / total) * 100);
return `
# ${epic.fields.summary}
Owner: ${epic.fields.assignee?.displayName || 'Unassigned'}
Status: ${epic.fields.status.name}
Start Date: ${epic.fields.customfield_startDate || 'Not set'}
Target Date: ${epic.fields.duedate || 'Not set'}
## Problem and Goal
${epic.fields.description || 'No description provided.'}
## Progress
Completed: ${done} of ${total} (${donePercent}%)
In Progress: ${inProgress}
To Do: ${todo}
## Key Work Items
${issues.slice(0, 5).map(i => `- ${i.key}: ${i.fields.summary} (${i.fields.status.name})`).join('\n')}
## Blockers
${issues.filter(i => i.fields.labels?.includes('blocker')).map(i => `- ${i.key}: ${i.fields.summary}`).join('\n') || 'None'}
## Next Steps
Continue execution on in-progress items. Unblock any blocked work. Target completion by ${epic.fields.duedate || 'TBD'}.
`.trim();
}This narrative is human-readable and ready to feed into Preso's deck generator. Preso will parse it, design the slides, apply your brand kit, and generate a finished deck.
Once your workflow is set up, you can trigger deck generation in several ways:
Manual trigger: Your team runs a command or clicks a button in your agent interface to generate a deck from a specific epic.
Scheduled: A cron job or scheduled workflow runs every Monday morning and generates an update deck for each active epic.
Event-driven: When an epic status changes to "Done" or when a sprint completes, automatically generate a stakeholder update deck.
Webhook-based: Jira webhooks notify your system when an epic is updated. Your system checks if the update is significant (status change, due date change) and triggers deck generation.
Here is an example of a scheduled trigger using a Node.js cron job:
const cron = require('node-cron');
const axios = require('axios');
// Every Monday at 9 AM, generate update decks for all active epics
cron.schedule('0 9 * * 1', async () => {
const epics = await getActiveEpics(); // Your function to fetch active epics from Jira
for (const epic of epics) {
const narrative = await buildNarrative(epic);
const deck = await preso.createPresentation({
narrative: narrative,
brand_kit_id: 'your-brand-kit-id',
format: 'stakeholder_update',
});
// Send the deck link to stakeholders via email or Slack
await notifyStakeholders(epic, deck.url);
}
});This automation saves hours every week. Your stakeholders get a fresh, professional update deck without anyone manually building it.
Once Preso generates your deck, you have several options:
Edit in Preso: Open the deck in Preso's editor to refine slides, adjust layouts, or add speaker notes before presenting.
Export to PowerPoint: Download the deck as a .pptx file and edit it in PowerPoint, Keynote, or Google Slides if needed.
Export to PDF: Share a PDF version with stakeholders who do not need to edit.
Share a link: Send stakeholders a secure link to view the deck in Preso. They can comment and provide feedback without downloading.
Publish to your stack: If you have integrations set up, Preso can push the deck to your Google Drive, OneDrive, or other storage. See Preso's integrations to connect your tools.
For sales teams and agencies, sales and revenue decks generated from data are especially powerful because they are personalized and on-brand. For startups, SaaS and startups decks like investor updates and board decks benefit from this workflow too.
Tip 1: Enrich Your Narrative With Custom Fields
Jira custom fields often contain valuable context that standard fields miss. If your team uses a custom field like "Business Impact" or "Customer Feedback," include it in your narrative:
const impact = epic.fields.customfield_businessImpact || 'Not specified';
narrative += `\nBusiness Impact:\n${impact}\n`;Tip 2: Summarize Child Issues Intelligently
Do not just list every child issue. Group them by status or theme:
const doneIssues = issues.filter(i => i.fields.status.name === 'Done');
const inProgressIssues = issues.filter(i => i.fields.status.name === 'In Progress');
const blockedIssues = issues.filter(i => i.fields.labels?.includes('blocker'));
narrative += `
## Completed
${doneIssues.map(i => `- ${i.key}: ${i.fields.summary}`).join('\n')}
## In Progress
${inProgressIssues.map(i => `- ${i.key}: ${i.fields.summary}`).join('\n')}
## Blocked
${blockedIssues.map(i => `- ${i.key}: ${i.fields.summary}`).join('\n')}
`;Tip 3: Use Preso's Brand Kit to Stay Consistent
Every deck generated from this workflow should match your brand. Set up on-brand features in Preso once, then every generated deck automatically applies your colors, fonts, logo, and voice. This is especially important if multiple teams are generating decks.
Tip 4: Add Speaker Notes Programmatically
Preso supports speaker notes. When building your narrative, include a speaker notes section:
narrative += `
## Speaker Notes
Slide 1: Welcome the team and set context for the epic.
Slide 2: Emphasize the business value of this work.
Slide 3: Highlight progress and celebrate completed work.
Slide 4: Be honest about blockers and mitigation plans.
Slide 5: Set clear expectations for next steps.
`;Tip 5: Handle Large Epics Gracefully
If an epic has 50+ child issues, listing them all makes the deck overwhelming. Instead, show summary statistics and link to Jira for details:
const totalIssues = issues.length;
const doneCount = issues.filter(i => i.fields.status.name === 'Done').length;
const progressPercent = Math.round((doneCount / totalIssues) * 100);
narrative += `
Progress: ${doneCount} of ${totalIssues} issues complete (${progressPercent}%)
For a detailed breakdown, view the epic in Jira: ${epic.self}
`;Preso will create a clean slide with the key metrics, and stakeholders can drill into Jira if they need granular details.
Issue: MCP server is not connecting to Jira.
Check that your Jira API token is valid and has not expired. Verify that your Jira instance URL is correct and accessible from the machine running the MCP server. Test the connection with a curl request:
curl -u [email protected]:YOUR_API_TOKEN \
"https://yourcompany.atlassian.net/rest/api/3/myself"
If this returns your user info, the token is valid.
Issue: Preso is not generating the deck.
Check that your brand kit ID is correct and that your Preso API credentials are valid. Make sure the narrative you are sending is valid plain text or markdown. If the narrative is very long (over 5000 characters), Preso may take longer to generate. Be patient.
Issue: The generated deck does not match my brand.
Ensure that your brand kit is fully configured in Preso. Check that the brand kit ID you are passing to the API matches the ID in your Preso account. If you recently updated your brand kit, clear your browser cache or regenerate the deck.
Issue: Child issues are not showing up in the narrative.
Verify that the child issues are properly linked to the epic in Jira. Use the Jira REST API to fetch the epic and check the response. If the response does not include child issues, they may not be linked correctly.
Let us walk through a complete example. Your startup has a critical epic: "Launch Payment Processing Integration." You need to present progress to your board next week.
Your epic in Jira has:
You run your MCP workflow:
Your board sees a professional, data-backed update in seconds. No manual work. No generic template. Just a deck that tells the real story of your work.
For more examples of how teams use Preso to automate deck generation, see Preso case studies.
Once you have the basic workflow running, you can scale it:
Multiple epics: Generate update decks for all active epics every sprint or on demand.
Multiple teams: Each team can have its own MCP configuration and brand kit. Preso respects each team's branding.
Custom formats: Create different narrative templates for different audiences. A board deck emphasizes strategic impact. A team standup emphasizes blockers and next steps. A customer update emphasizes shipped value.
Integration with other tools: Use MCP to pull data from other sources too. Combine Jira data with Slack messages, GitHub commits, or Google Sheets data to build richer narratives.
For enterprise teams, headless presentations is the right approach. Your product or workflow generates decks on demand, without anyone manually building them.
The blank slide is gone. The afternoon lost to PowerPoint formatting is gone. The generic template is gone. You have a system that turns tracked work into presented work, on-brand and ready to ship.
Ready to generate your first deck from a Jira epic?
Your next stakeholder update is waiting. Build it with Preso and see how much faster you can ship a presentation that actually looks designed.