Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 8 additions & 87 deletions docs/src/content/docs/reference/custom-safe-outputs.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,7 @@ sidebar:
order: 5
---

Custom safe outputs extend GitHub Agentic Workflows beyond built-in GitHub operations. While built-in safe outputs handle GitHub issues, PRs, and discussions, custom safe outputs let you integrate with third-party services like Notion, Slack, databases, or any external API.

## When to Use Custom Safe Outputs

Use custom safe outputs when you need to:

- Send data to external services (Slack, Discord, Notion, Jira)
- Trigger deployments or CI/CD pipelines
- Update databases or external storage
- Call custom APIs that require authentication
- Perform any write operation that built-in safe outputs don't cover
Custom safe outputs extend built-in GitHub operations to integrate with third-party services — Slack, Discord, Notion, Jira, databases, or any external API requiring authentication. Use them for any write operation that built-in safe outputs don't cover.
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The updated intro sentence is a bit misleading: custom safe outputs don’t really “extend built-in GitHub operations” so much as extend GitHub Agentic Workflows / built-in safe outputs to support third‑party write operations. Consider rephrasing to reference “built-in safe outputs” (issues/PRs/discussions) to avoid implying this is a GitHub-native feature set being extended.

Suggested change
Custom safe outputs extend built-in GitHub operations to integrate with third-party services — Slack, Discord, Notion, Jira, databases, or any external API requiring authentication. Use them for any write operation that built-in safe outputs don't cover.
Custom safe outputs extend GitHub Agentic Workflows' built-in safe outputs (issues, pull requests, discussions) to support third-party services — Slack, Discord, Notion, Jira, databases, or any external API requiring authentication. Use them for any write operation that built-in safe outputs don't cover.

Copilot uses AI. Check for mistakes.

## Quick Start

Expand Down Expand Up @@ -95,30 +85,9 @@ Custom safe outputs separate read and write operations: agents use read-only Mod

## Creating a Custom Safe Output

### Step 1: Define the MCP Server (Read-Only)
### Step 1: Define the Shared Configuration

Create a shared configuration with read-only MCP tools:

```yaml wrap
---
mcp-servers:
notion:
container: "mcp/notion"
env:
NOTION_TOKEN: "${{ secrets.NOTION_TOKEN }}"
allowed:
- "search_pages"
- "get_page"
- "get_database"
- "query_database"
---
```

Use `container:` for Docker servers or `command:`/`args:` for npx. List only read-only tools in `allowed`.

### Step 2: Define the Custom Job (Write Operations)

Add a custom job under `safe-outputs.jobs` for write operations:
In a shared file, define the read-only MCP server and the custom job together:

```yaml wrap
---
Expand Down Expand Up @@ -218,9 +187,9 @@ safe-outputs:
---
```

All jobs require `description` and `inputs`. Use `output` for success messages and `actions/github-script@v8` for API calls with `core.setFailed()` error handling.
Use `container:` for Docker servers or `command:`/`args:` for npx. List only read-only tools in `allowed`. All jobs require `description` and `inputs`. Use `output` for success messages and `actions/github-script@v8` for API calls with `core.setFailed()` error handling.

### Step 3: Use in Workflow
### Step 2: Use in Workflow

Import the configuration:

Expand Down Expand Up @@ -317,14 +286,13 @@ Custom safe-output jobs receive the agent's data through the `GH_AW_AGENT_OUTPUT

The `type` field matches your job name with dashes converted to underscores (e.g., job `webhook-notify` → type `webhook_notify`).

#### Bash Example
#### Example

```yaml
steps:
- name: Process output
run: |
if [ -f "$GH_AW_AGENT_OUTPUT" ]; then
# Extract specific field from matching items
MESSAGE=$(cat "$GH_AW_AGENT_OUTPUT" | jq -r '.items[] | select(.type == "my_job") | .message')
echo "Message: $MESSAGE"
else
Expand All @@ -333,45 +301,7 @@ steps:
fi
```

#### JavaScript Example

```yaml
steps:
- name: Process output
uses: actions/github-script@v8
with:
script: |
const fs = require('fs');
const outputFile = process.env.GH_AW_AGENT_OUTPUT;

if (!outputFile) {
core.info('No GH_AW_AGENT_OUTPUT environment variable found');
return;
}

// Read and parse the JSON file
const fileContent = fs.readFileSync(outputFile, 'utf8');
const agentOutput = JSON.parse(fileContent);

// Filter for items matching this job (job-name → job_name)
const items = agentOutput.items.filter(item => item.type === 'my_job');

// Process each item
for (const item of items) {
const message = item.message;
core.info(`Processing: ${message}`);
// Your logic here
}
```

### Understanding `inputs:`

The `inputs:` field in your job definition serves **two purposes**:

1. **Tool Discovery**: Defines the MCP tool schema that the AI agent sees
2. **Validation**: Describes what fields the agent should provide in its output

The agent uses the `inputs:` schema to understand what parameters to include when calling your custom job. The actual values are written to the `GH_AW_AGENT_OUTPUT` JSON file, which your job must read and parse.
The `inputs:` schema serves as both the MCP tool definition visible to the agent and validation for the output fields written to `GH_AW_AGENT_OUTPUT`.

## Importing Custom Jobs

Expand Down Expand Up @@ -422,16 +352,7 @@ Store secrets in GitHub Secrets and pass via environment variables. Limit job pe

## Staged Mode Support

Check `GH_AW_SAFE_OUTPUTS_STAGED` to preview operations without executing:

```javascript
if (process.env.GH_AW_SAFE_OUTPUTS_STAGED === 'true') {
core.info('🎭 Staged mode: would send notification');
await core.summary.addRaw('## Preview\nWould send: ' + process.env.MESSAGE).write();
return;
}
// Actually send the notification
```
When `GH_AW_SAFE_OUTPUTS_STAGED === 'true'`, skip the real operation and display a preview using `core.summary`. See [Staged Mode](/gh-aw/reference/staged-mode/#staged-mode-for-custom-safe-output-jobs) for a complete example.
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This reads like the environment variable’s value literally includes a JavaScript comparison (=== 'true'). For language-agnostic docs, it’s clearer/accurate to say something like “When GH_AW_SAFE_OUTPUTS_STAGED is set to 'true'…” (the comparison operator is part of your code, not the variable).

Suggested change
When `GH_AW_SAFE_OUTPUTS_STAGED === 'true'`, skip the real operation and display a preview using `core.summary`. See [Staged Mode](/gh-aw/reference/staged-mode/#staged-mode-for-custom-safe-output-jobs) for a complete example.
When `GH_AW_SAFE_OUTPUTS_STAGED` is set to `'true'`, skip the real operation and display a preview using `core.summary`. See [Staged Mode](/gh-aw/reference/staged-mode/#staged-mode-for-custom-safe-output-jobs) for a complete example.

Copilot uses AI. Check for mistakes.

## Troubleshooting

Expand Down
Loading