Elsevier Capabilities

Naftiko 0.5 capability definitions for Elsevier - 100 capabilities showing integration workflows and service orchestrations.

Sort
Expand

Given an article abstract, calls the Anthropic API to generate a plain-language summary suitable for social media promotion, then stores the result in Salesforce.

naftiko: "0.5"
info:
  label: "AI-Assisted Abstract Summarization"
  description: "Given an article abstract, calls the Anthropic API to generate a plain-language summary suitable for social media promotion, then stores the result in Salesforce."
  tags:
    - ai
    - publishing
    - content
    - anthropic
    - salesforce
    - automation
capability:
  exposes:
    - type: mcp
      namespace: ai-content
      port: 8080
      tools:
        - name: summarize-abstract
          description: "Given an article DOI and abstract text, call Anthropic Claude to generate a plain-language summary and a social media teaser, then store both in the Salesforce article record. Use for post-acceptance content marketing preparation."
          inputParameters:
            - name: article_doi
              in: body
              type: string
              description: "Article DOI for the accepted manuscript."
            - name: abstract_text
              in: body
              type: string
              description: "Full abstract text from the manuscript."
          steps:
            - name: generate-summary
              type: call
              call: anthropic.create-message
              with:
                model: "claude-opus-4-5"
                max_tokens: "500"
                system: "You are a science communicator. Summarize the following abstract in plain language suitable for a general audience in 2-3 sentences, then provide a 280-character social media teaser."
                user_content: "{{abstract_text}}"
            - name: store-summary
              type: call
              call: salesforce.patch-article-summary
              with:
                doi__c: "{{article_doi}}"
                Plain_Language_Summary__c: "{{generate-summary.summary}}"
                Social_Teaser__c: "{{generate-summary.teaser}}"
  consumes:
    - type: http
      namespace: anthropic
      baseUri: "https://api.anthropic.com/v1"
      authentication:
        type: apikey
        key: "x-api-key"
        value: "$secrets.anthropic_api_key"
        placement: header
      resources:
        - name: messages
          path: "/messages"
          operations:
            - name: create-message
              method: POST
    - type: http
      namespace: salesforce
      baseUri: "https://elsevier.my.salesforce.com/services/data/v58.0"
      authentication:
        type: bearer
        token: "$secrets.salesforce_token"
      resources:
        - name: article-records
          path: "/query"
          inputParameters:
            - name: doi__c
              in: query
          operations:
            - name: patch-article-summary
              method: PATCH

Executes a SQL query against Amazon Redshift for content analytics lookups.

naftiko: "0.5"
info:
  label: "Amazon Redshift Analytics Query"
  description: "Executes a SQL query against Amazon Redshift for content analytics lookups."
  tags:
    - data
    - analytics
    - amazon-redshift
capability:
  exposes:
    - type: mcp
      namespace: data-ops
      port: 8080
      tools:
        - name: run-redshift-query
          description: "Given a SQL query, execute against Redshift and return results."
          inputParameters:
            - name: sql
              in: body
              type: string
              description: "SQL query."
          call: redshift.execute-statement
          with:
            sql: "{{sql}}"
            database: "content_analytics"
          outputParameters:
            - name: statement_id
              type: string
              mapping: "$.Id"
  consumes:
    - type: http
      namespace: redshift
      baseUri: "https://redshift-data.eu-west-1.amazonaws.com"
      authentication:
        type: aws-sigv4
        accessKeyId: "$secrets.aws_access_key"
        secretAccessKey: "$secrets.aws_secret_key"
      resources:
        - name: statements
          path: "/"
          operations:
            - name: execute-statement
              method: POST

Checks the depth of an SQS queue and returns message counts.

naftiko: "0.5"
info:
  label: "Amazon SQS Message Monitor"
  description: "Checks the depth of an SQS queue and returns message counts."
  tags:
    - engineering
    - messaging
    - amazon-sqs
capability:
  exposes:
    - type: mcp
      namespace: platform-ops
      port: 8080
      tools:
        - name: get-queue-depth
          description: "Given an SQS queue URL, return approximate message count."
          inputParameters:
            - name: queue_url
              in: body
              type: string
              description: "SQS queue URL."
          call: sqs.get-queue-attributes
          with:
            queue_url: "{{queue_url}}"
          outputParameters:
            - name: messages
              type: integer
              mapping: "$.Attributes.ApproximateNumberOfMessages"
  consumes:
    - type: http
      namespace: sqs
      baseUri: "https://sqs.eu-west-1.amazonaws.com"
      authentication:
        type: aws-sigv4
        accessKeyId: "$secrets.aws_access_key"
        secretAccessKey: "$secrets.aws_secret_key"
      resources:
        - name: queues
          path: "/"
          operations:
            - name: get-queue-attributes
              method: POST

Looks up an article DOI in Salesforce to verify the institution's subscription entitlement, returning access level and license terms.

naftiko: "0.5"
info:
  label: "Article Access License Check"
  description: "Looks up an article DOI in Salesforce to verify the institution's subscription entitlement, returning access level and license terms."
  tags:
    - publishing
    - licensing
    - crm
    - salesforce
capability:
  exposes:
    - type: mcp
      namespace: licensing
      port: 8080
      tools:
        - name: get-article-access
          description: "Given an article DOI and an institution ID, check the Salesforce subscription record to determine access level (open-access, subscribed, pay-per-view). Use when resolving reader access disputes."
          inputParameters:
            - name: doi
              in: body
              type: string
              description: "Article DOI in the format 10.XXXX/YYYY."
            - name: institution_id
              in: body
              type: string
              description: "Salesforce account ID for the subscribing institution."
          call: salesforce.get-entitlement
          with:
            doi__c: "{{doi}}"
            account_id: "{{institution_id}}"
          outputParameters:
            - name: access_level
              type: string
              mapping: "$.records[0].Access_Level__c"
            - name: license_type
              type: string
              mapping: "$.records[0].License_Type__c"
            - name: expiry_date
              type: string
              mapping: "$.records[0].License_Expiry__c"
  consumes:
    - type: http
      namespace: salesforce
      baseUri: "https://elsevier.my.salesforce.com/services/data/v58.0"
      authentication:
        type: bearer
        token: "$secrets.salesforce_token"
      resources:
        - name: entitlements
          path: "/query"
          inputParameters:
            - name: doi__c
              in: query
            - name: account_id
              in: query
          operations:
            - name: get-entitlement
              method: GET

When a manuscript is submitted via the editorial system, creates a Salesforce opportunity record for the journal, assigns a handling editor in Workday, and sends a confirmation via Microsoft Teams.

naftiko: "0.5"
info:
  label: "Author Submission Intake"
  description: "When a manuscript is submitted via the editorial system, creates a Salesforce opportunity record for the journal, assigns a handling editor in Workday, and sends a confirmation via Microsoft Teams."
  tags:
    - publishing
    - crm
    - hr
    - salesforce
    - workday
    - msteams
    - onboarding
capability:
  exposes:
    - type: mcp
      namespace: publishing-intake
      port: 8080
      tools:
        - name: create-submission-record
          description: "Given a manuscript ID and journal code, create a Salesforce opportunity for the submission, look up the handling editor in Workday, and send a Teams notification to the editorial team."
          inputParameters:
            - name: manuscript_id
              in: body
              type: string
              description: "Unique manuscript identifier from the editorial submission system."
            - name: journal_code
              in: body
              type: string
              description: "Short journal code (e.g., JBC, CELL) identifying the target publication."
            - name: author_email
              in: body
              type: string
              description: "Corresponding author email address for record linkage."
          steps:
            - name: create-opportunity
              type: call
              call: salesforce.create-opportunity
              with:
                Name: "Manuscript {{manuscript_id}} — {{journal_code}}"
                StageName: "Submission Review"
                CloseDate: "2026-12-31"
                Journal_Code__c: "{{journal_code}}"
            - name: get-editor
              type: call
              call: workday.get-worker-by-role
              with:
                role: "handling_editor"
                department: "{{journal_code}}"
            - name: notify-team
              type: call
              call: msteams.send-message
              with:
                channel: "editorial-alerts"
                text: "New submission {{manuscript_id}} received for {{journal_code}}. Assigned editor: {{get-editor.full_name}}. SF record: {{create-opportunity.id}}"
  consumes:
    - type: http
      namespace: salesforce
      baseUri: "https://elsevier.my.salesforce.com/services/data/v58.0"
      authentication:
        type: bearer
        token: "$secrets.salesforce_token"
      resources:
        - name: opportunities
          path: "/sobjects/Opportunity"
          operations:
            - name: create-opportunity
              method: POST
    - type: http
      namespace: workday
      baseUri: "https://wd2-impl-services1.workday.com/ccx/api/v1"
      authentication:
        type: bearer
        token: "$secrets.workday_token"
      resources:
        - name: workers
          path: "/workers"
          inputParameters:
            - name: role
              in: query
            - name: department
              in: query
          operations:
            - name: get-worker-by-role
              method: GET
    - type: http
      namespace: msteams
      baseUri: "https://graph.microsoft.com/v1.0"
      authentication:
        type: bearer
        token: "$secrets.msteams_token"
      resources:
        - name: channel-messages
          path: "/teams/editorial/channels/general/messages"
          operations:
            - name: send-message
              method: POST

Searches CloudWatch Logs by filter pattern and returns matching log events.

naftiko: "0.5"
info:
  label: "AWS CloudWatch Log Search"
  description: "Searches CloudWatch Logs by filter pattern and returns matching log events."
  tags:
    - engineering
    - monitoring
    - aws
    - cloudwatch
capability:
  exposes:
    - type: mcp
      namespace: platform-ops
      port: 8080
      tools:
        - name: search-logs
          description: "Given a log group and filter pattern, return matching CloudWatch log events."
          inputParameters:
            - name: log_group
              in: body
              type: string
              description: "CloudWatch log group name."
            - name: filter_pattern
              in: body
              type: string
              description: "Filter pattern."
          call: cloudwatch.filter-log-events
          with:
            log_group: "{{log_group}}"
            filter_pattern: "{{filter_pattern}}"
          outputParameters:
            - name: events
              type: array
              mapping: "$.events"
  consumes:
    - type: http
      namespace: cloudwatch
      baseUri: "https://logs.eu-west-1.amazonaws.com"
      authentication:
        type: aws-sigv4
        accessKeyId: "$secrets.aws_access_key"
        secretAccessKey: "$secrets.aws_secret_key"
      resources:
        - name: logs
          path: "/"
          operations:
            - name: filter-log-events
              method: POST

Retrieves the running state of an EC2 instance by ID.

naftiko: "0.5"
info:
  label: "AWS EC2 Instance Status"
  description: "Retrieves the running state of an EC2 instance by ID."
  tags:
    - cloud
    - infrastructure
    - aws
    - ec2
capability:
  exposes:
    - type: mcp
      namespace: cloud-ops
      port: 8080
      tools:
        - name: get-ec2-status
          description: "Given an EC2 instance ID, return the state and instance type."
          inputParameters:
            - name: instance_id
              in: body
              type: string
              description: "EC2 instance ID."
          call: ec2.describe-instance
          with:
            instance_id: "{{instance_id}}"
          outputParameters:
            - name: state
              type: string
              mapping: "$.Reservations[0].Instances[0].State.Name"
            - name: instance_type
              type: string
              mapping: "$.Reservations[0].Instances[0].InstanceType"
  consumes:
    - type: http
      namespace: ec2
      baseUri: "https://ec2.eu-west-1.amazonaws.com"
      authentication:
        type: aws-sigv4
        accessKeyId: "$secrets.aws_access_key"
        secretAccessKey: "$secrets.aws_secret_key"
      resources:
        - name: instances
          path: "/"
          operations:
            - name: describe-instance
              method: GET

Invokes an AWS Lambda function to resolve a DOI and return the canonical article URL and metadata.

naftiko: "0.5"
info:
  label: "AWS Lambda DOI Resolver"
  description: "Invokes an AWS Lambda function to resolve a DOI and return the canonical article URL and metadata."
  tags:
    - publishing
    - serverless
    - aws
    - aws-lambda
capability:
  exposes:
    - type: mcp
      namespace: publishing-ops
      port: 8080
      tools:
        - name: resolve-doi
          description: "Given a DOI, invoke the DOI resolver Lambda and return the canonical URL."
          inputParameters:
            - name: doi
              in: body
              type: string
              description: "Article DOI."
          call: lambda.invoke-function
          with:
            function_name: "doi-resolver"
            payload: "{\"doi\": \"{{doi}}\"}"
          outputParameters:
            - name: canonical_url
              type: string
              mapping: "$.body.url"
            - name: title
              type: string
              mapping: "$.body.title"
  consumes:
    - type: http
      namespace: lambda
      baseUri: "https://lambda.eu-west-1.amazonaws.com/2015-03-31"
      authentication:
        type: aws-sigv4
        accessKeyId: "$secrets.aws_access_key"
        secretAccessKey: "$secrets.aws_secret_key"
      resources:
        - name: functions
          path: "/functions/doi-resolver/invocations"
          operations:
            - name: invoke-function
              method: POST

Triggers a content backup job to S3 and posts confirmation to the platform ops Slack channel.

naftiko: "0.5"
info:
  label: "AWS S3 Content Backup"
  description: "Triggers a content backup job to S3 and posts confirmation to the platform ops Slack channel."
  tags:
    - data
    - storage
    - amazon-s3
    - slack
capability:
  exposes:
    - type: mcp
      namespace: backup-ops
      port: 8080
      tools:
        - name: trigger-content-backup
          description: "Trigger a content backup to S3 and notify ops via Slack."
          inputParameters:
            - name: bucket
              in: body
              type: string
              description: "S3 backup bucket."
            - name: prefix
              in: body
              type: string
              description: "S3 key prefix for the backup."
          steps:
            - name: start-backup
              type: call
              call: s3.initiate-backup
              with:
                bucket: "{{bucket}}"
                prefix: "{{prefix}}"
            - name: notify-ops
              type: call
              call: slack.post-message
              with:
                channel: "platform-ops"
                text: "Content backup initiated to s3://{{bucket}}/{{prefix}}"
  consumes:
    - type: http
      namespace: s3
      baseUri: "https://s3.eu-west-1.amazonaws.com"
      authentication:
        type: aws-sigv4
        accessKeyId: "$secrets.aws_access_key"
        secretAccessKey: "$secrets.aws_secret_key"
      resources:
        - name: objects
          path: "/{{bucket}}/{{prefix}}"
          inputParameters:
            - name: bucket
              in: path
            - name: prefix
              in: path
          operations:
            - name: initiate-backup
              method: PUT
    - type: http
      namespace: slack
      baseUri: "https://slack.com/api"
      authentication:
        type: bearer
        token: "$secrets.slack_bot_token"
      resources:
        - name: messages
          path: "/chat.postMessage"
          operations:
            - name: post-message
              method: POST

Updates Azure AD group memberships when an employee changes teams in Workday.

naftiko: "0.5"
info:
  label: "Azure AD Group Sync"
  description: "Updates Azure AD group memberships when an employee changes teams in Workday."
  tags:
    - identity
    - hr
    - azure-active-directory
    - workday
capability:
  exposes:
    - type: mcp
      namespace: iam-ops
      port: 8080
      tools:
        - name: sync-ad-groups
          description: "Given a Workday employee ID and new team, update Azure AD group memberships."
          inputParameters:
            - name: employee_id
              in: body
              type: string
              description: "Workday employee ID."
            - name: new_team
              in: body
              type: string
              description: "New team code."
          steps:
            - name: get-worker
              type: call
              call: workday.get-worker
              with:
                employee_id: "{{employee_id}}"
            - name: update-groups
              type: call
              call: azuread.update-membership
              with:
                user_email: "{{get-worker.email}}"
                team: "{{new_team}}"
  consumes:
    - type: http
      namespace: workday
      baseUri: "https://wd5-impl-services1.workday.com/ccx/service/elsevier"
      authentication:
        type: basic
        username: "$secrets.workday_user"
        password: "$secrets.workday_password"
      resources:
        - name: workers
          path: "/Human_Resources/v40.0/Get_Workers"
          operations:
            - name: get-worker
              method: GET
    - type: http
      namespace: azuread
      baseUri: "https://graph.microsoft.com/v1.0"
      authentication:
        type: bearer
        token: "$secrets.msgraph_token"
      resources:
        - name: groups
          path: "/groups"
          operations:
            - name: update-membership
              method: POST

Uploads a finalized manuscript PDF to Azure Blob Storage for long-term archival and logs the action to ServiceNow.

naftiko: "0.5"
info:
  label: "Azure Blob Manuscript Archive"
  description: "Uploads a finalized manuscript PDF to Azure Blob Storage for long-term archival and logs the action to ServiceNow."
  tags:
    - publishing
    - storage
    - azure-blob-storage
    - servicenow
capability:
  exposes:
    - type: mcp
      namespace: archive-ops
      port: 8080
      tools:
        - name: archive-manuscript
          description: "Given a manuscript ID and source URL, upload to Azure Blob Storage and create a ServiceNow archival record."
          inputParameters:
            - name: manuscript_id
              in: body
              type: string
              description: "Manuscript ID."
            - name: source_url
              in: body
              type: string
              description: "Source URL of the manuscript PDF."
          steps:
            - name: upload-blob
              type: call
              call: azure-blob.upload
              with:
                container: "manuscripts-archive"
                blob_name: "{{manuscript_id}}.pdf"
                source_url: "{{source_url}}"
            - name: log-archival
              type: call
              call: servicenow.create-record
              with:
                table: "u_manuscript_archive"
                manuscript_id: "{{manuscript_id}}"
                blob_url: "{{upload-blob.url}}"
  consumes:
    - type: http
      namespace: azure-blob
      baseUri: "https://elsevierstorage.blob.core.windows.net"
      authentication:
        type: bearer
        token: "$secrets.azure_storage_token"
      resources:
        - name: blobs
          path: "/{{container}}/{{blob_name}}"
          inputParameters:
            - name: container
              in: path
            - name: blob_name
              in: path
          operations:
            - name: upload
              method: PUT
    - type: http
      namespace: servicenow
      baseUri: "https://elsevier.service-now.com/api/now"
      authentication:
        type: basic
        username: "$secrets.servicenow_user"
        password: "$secrets.servicenow_password"
      resources:
        - name: records
          path: "/table/{{table}}"
          inputParameters:
            - name: table
              in: path
          operations:
            - name: create-record
              method: POST

Monitors Azure consumption for cost spikes above a daily threshold, writes anomalies to Snowflake, and pages the cloud operations team via ServiceNow and Teams.

naftiko: "0.5"
info:
  label: "Azure Cloud Cost Anomaly Alert"
  description: "Monitors Azure consumption for cost spikes above a daily threshold, writes anomalies to Snowflake, and pages the cloud operations team via ServiceNow and Teams."
  tags:
    - cloud
    - finops
    - azure
    - snowflake
    - servicenow
    - msteams
    - cost-management
capability:
  exposes:
    - type: mcp
      namespace: cloud-finops
      port: 8080
      tools:
        - name: alert-cost-anomaly
          description: "Given an Azure subscription ID and a daily spend threshold in USD, fetch the current day's cost from Azure Cost Management, record the anomaly in Snowflake, and open a ServiceNow incident if the threshold is exceeded."
          inputParameters:
            - name: subscription_id
              in: body
              type: string
              description: "Azure subscription ID to monitor."
            - name: daily_threshold_usd
              in: body
              type: number
              description: "Daily spend threshold in USD above which an alert is triggered."
          steps:
            - name: get-azure-cost
              type: call
              call: azure.get-daily-cost
              with:
                subscription_id: "{{subscription_id}}"
            - name: write-cost-record
              type: call
              call: snowflake.insert-cost-row
              with:
                subscription_id: "{{subscription_id}}"
                daily_cost: "{{get-azure-cost.total_cost}}"
            - name: create-incident
              type: call
              call: servicenow.create-incident
              with:
                short_description: "Azure cost anomaly: subscription {{subscription_id}} exceeded ${{daily_threshold_usd}}/day"
                urgency: "2"
                description: "Current daily spend: ${{get-azure-cost.total_cost}}. Threshold: ${{daily_threshold_usd}}."
  consumes:
    - type: http
      namespace: azure
      baseUri: "https://management.azure.com"
      authentication:
        type: bearer
        token: "$secrets.azure_token"
      resources:
        - name: cost-query
          path: "/subscriptions/{{subscription_id}}/providers/Microsoft.CostManagement/query"
          inputParameters:
            - name: subscription_id
              in: path
          operations:
            - name: get-daily-cost
              method: POST
    - type: http
      namespace: snowflake
      baseUri: "https://elsevier.snowflakecomputing.com/api/v2"
      authentication:
        type: bearer
        token: "$secrets.snowflake_token"
      resources:
        - name: cost-records
          path: "/statements"
          operations:
            - name: insert-cost-row
              method: POST
    - type: http
      namespace: servicenow
      baseUri: "https://elsevier.service-now.com/api/now"
      authentication:
        type: basic
        username: "$secrets.servicenow_user"
        password: "$secrets.servicenow_password"
      resources:
        - name: incidents
          path: "/table/incident"
          operations:
            - name: create-incident
              method: POST

Retrieves the latest build result for an Azure DevOps pipeline.

naftiko: "0.5"
info:
  label: "Azure DevOps Build Monitor"
  description: "Retrieves the latest build result for an Azure DevOps pipeline."
  tags:
    - engineering
    - cicd
    - azure-devops
capability:
  exposes:
    - type: mcp
      namespace: cicd-ops
      port: 8080
      tools:
        - name: get-build-result
          description: "Given an Azure DevOps project and pipeline, return the latest build result."
          inputParameters:
            - name: project
              in: body
              type: string
              description: "Azure DevOps project."
            - name: pipeline_id
              in: body
              type: string
              description: "Pipeline ID."
          call: azdo.get-latest-build
          with:
            project: "{{project}}"
            definition_id: "{{pipeline_id}}"
          outputParameters:
            - name: result
              type: string
              mapping: "$.value[0].result"
            - name: commit
              type: string
              mapping: "$.value[0].sourceVersion"
  consumes:
    - type: http
      namespace: azdo
      baseUri: "https://dev.azure.com/elsevier"
      authentication:
        type: basic
        username: ""
        password: "$secrets.azdo_pat"
      resources:
        - name: builds
          path: "/{{project}}/_apis/build/builds"
          inputParameters:
            - name: project
              in: path
          operations:
            - name: get-latest-build
              method: GET

Purges Cloudflare CDN cache for specific URLs and notifies the platform team via Slack.

naftiko: "0.5"
info:
  label: "Cloudflare CDN Purge"
  description: "Purges Cloudflare CDN cache for specific URLs and notifies the platform team via Slack."
  tags:
    - infrastructure
    - cdn
    - cloudflare
    - slack
capability:
  exposes:
    - type: mcp
      namespace: platform-ops
      port: 8080
      tools:
        - name: purge-cdn-cache
          description: "Given a Cloudflare zone ID and list of URLs, purge CDN cache and notify Slack."
          inputParameters:
            - name: zone_id
              in: body
              type: string
              description: "Cloudflare zone ID."
            - name: urls
              in: body
              type: string
              description: "Comma-separated URLs to purge."
          steps:
            - name: purge-cache
              type: call
              call: cloudflare.purge-cache
              with:
                zone_id: "{{zone_id}}"
                files: "{{urls}}"
            - name: notify-team
              type: call
              call: slack.post-message
              with:
                channel: "platform-ops"
                text: "CDN cache purged for zone {{zone_id}}: {{urls}}"
  consumes:
    - type: http
      namespace: cloudflare
      baseUri: "https://api.cloudflare.com/client/v4"
      authentication:
        type: bearer
        token: "$secrets.cloudflare_token"
      resources:
        - name: purge
          path: "/zones/{{zone_id}}/purge_cache"
          inputParameters:
            - name: zone_id
              in: path
          operations:
            - name: purge-cache
              method: POST
    - type: http
      namespace: slack
      baseUri: "https://slack.com/api"
      authentication:
        type: bearer
        token: "$secrets.slack_bot_token"
      resources:
        - name: messages
          path: "/chat.postMessage"
          operations:
            - name: post-message
              method: POST

Queries SAP Concur for expense reports pending approval beyond five business days, and sends reminder messages via Teams to the respective approvers.

naftiko: "0.5"
info:
  label: "Concur Expense Report Approval Reminder"
  description: "Queries SAP Concur for expense reports pending approval beyond five business days, and sends reminder messages via Teams to the respective approvers."
  tags:
    - finance
    - expense-management
    - sap-concur
    - msteams
    - approval
capability:
  exposes:
    - type: mcp
      namespace: expense-ops
      port: 8080
      tools:
        - name: send-expense-approval-reminders
          description: "Query SAP Concur for expense reports that have been pending approval for more than a configurable number of days, then send a Teams reminder to each overdue approver. Use daily as part of finance operations."
          inputParameters:
            - name: pending_days_threshold
              in: body
              type: integer
              description: "Number of business days an expense report can be pending before a reminder is sent (e.g., 5)."
          steps:
            - name: get-pending-reports
              type: call
              call: concur.get-pending-expense-reports
              with:
                pending_days: "{{pending_days_threshold}}"
            - name: send-reminders
              type: call
              call: msteams.send-message
              with:
                channel: "finance-ops"
                text: "Expense approval reminder: {{get-pending-reports.report_count}} reports pending over {{pending_days_threshold}} days. Approvers: {{get-pending-reports.approver_list}}"
  consumes:
    - type: http
      namespace: concur
      baseUri: "https://www.concursolutions.com/api/v3.0"
      authentication:
        type: bearer
        token: "$secrets.concur_token"
      resources:
        - name: expense-reports
          path: "/expense/reports"
          inputParameters:
            - name: pending_days
              in: query
          operations:
            - name: get-pending-expense-reports
              method: GET
    - type: http
      namespace: msteams
      baseUri: "https://graph.microsoft.com/v1.0"
      authentication:
        type: bearer
        token: "$secrets.msteams_token"
      resources:
        - name: channel-messages
          path: "/teams/finance/channels/general/messages"
          operations:
            - name: send-message
              method: POST

Periodically audits Confluence knowledge base articles older than 180 days without updates, creates Jira tasks for content owners to review, and reports findings to the IT knowledge management team.

naftiko: "0.5"
info:
  label: "Confluence Knowledge Article Quality Scan"
  description: "Periodically audits Confluence knowledge base articles older than 180 days without updates, creates Jira tasks for content owners to review, and reports findings to the IT knowledge management team."
  tags:
    - itsm
    - knowledge-management
    - confluence
    - jira
    - msteams
capability:
  exposes:
    - type: mcp
      namespace: knowledge-ops
      port: 8080
      tools:
        - name: audit-stale-kb-articles
          description: "Given a Confluence space key and a staleness threshold in days, list pages not updated within the threshold, create a Jira task for each stale article owner, and post a summary to the IT Teams channel."
          inputParameters:
            - name: confluence_space_key
              in: body
              type: string
              description: "Confluence space key to audit (e.g., ITSUPPORT)."
            - name: stale_days_threshold
              in: body
              type: integer
              description: "Number of days since last update before an article is considered stale (e.g., 180)."
          steps:
            - name: list-stale-pages
              type: call
              call: confluence.get-stale-pages
              with:
                space_key: "{{confluence_space_key}}"
                days: "{{stale_days_threshold}}"
            - name: create-review-tasks
              type: call
              call: jira.create-issue
              with:
                project_key: "KM"
                issuetype: "Task"
                summary: "KB article review: {{list-stale-pages.stale_count}} stale pages in {{confluence_space_key}}"
                description: "Pages requiring review: {{list-stale-pages.page_titles}}"
            - name: notify-team
              type: call
              call: msteams.send-message
              with:
                channel: "it-knowledge-mgmt"
                text: "KB audit: {{list-stale-pages.stale_count}} articles in {{confluence_space_key}} not updated in {{stale_days_threshold}} days. Jira: {{create-review-tasks.key}}"
  consumes:
    - type: http
      namespace: confluence
      baseUri: "https://elsevier.atlassian.net/wiki/rest/api"
      authentication:
        type: basic
        username: "$secrets.confluence_user"
        password: "$secrets.confluence_api_token"
      resources:
        - name: pages
          path: "/space/{{space_key}}/content/page"
          inputParameters:
            - name: space_key
              in: path
            - name: days
              in: query
          operations:
            - name: get-stale-pages
              method: GET
    - type: http
      namespace: jira
      baseUri: "https://elsevier.atlassian.net/rest/api/3"
      authentication:
        type: basic
        username: "$secrets.jira_user"
        password: "$secrets.jira_api_token"
      resources:
        - name: issues
          path: "/issue"
          operations:
            - name: create-issue
              method: POST
    - type: http
      namespace: msteams
      baseUri: "https://graph.microsoft.com/v1.0"
      authentication:
        type: bearer
        token: "$secrets.msteams_token"
      resources:
        - name: channel-messages
          path: "/teams/it/channels/knowledge-mgmt/messages"
          operations:
            - name: send-message
              method: POST

Searches the Confluence knowledge base by keyword and returns matching page titles and URLs.

naftiko: "0.5"
info:
  label: "Confluence Knowledge Base Search"
  description: "Searches the Confluence knowledge base by keyword and returns matching page titles and URLs."
  tags:
    - documentation
    - collaboration
    - confluence
capability:
  exposes:
    - type: mcp
      namespace: docs-ops
      port: 8080
      tools:
        - name: search-kb
          description: "Given a search keyword, find matching knowledge base articles in Confluence."
          inputParameters:
            - name: keyword
              in: body
              type: string
              description: "Search keyword."
          call: confluence.search
          with:
            cql: "type=page AND text~'{{keyword}}'"
          outputParameters:
            - name: results
              type: array
              mapping: "$.results[*]"
  consumes:
    - type: http
      namespace: confluence
      baseUri: "https://elsevier.atlassian.net/wiki/rest/api"
      authentication:
        type: basic
        username: "$secrets.confluence_user"
        password: "$secrets.confluence_token"
      resources:
        - name: search
          path: "/search"
          operations:
            - name: search
              method: GET

When Datadog detects a content delivery error spike, creates a ServiceNow incident, notifies the platform engineering channel in Teams, and opens a Jira bug for root-cause tracking.

naftiko: "0.5"
info:
  label: "Content Platform Incident Response"
  description: "When Datadog detects a content delivery error spike, creates a ServiceNow incident, notifies the platform engineering channel in Teams, and opens a Jira bug for root-cause tracking."
  tags:
    - itsm
    - incident-response
    - datadog
    - servicenow
    - jira
    - msteams
    - devops
capability:
  exposes:
    - type: mcp
      namespace: platform-ops
      port: 8080
      tools:
        - name: handle-content-incident
          description: "Given a Datadog alert ID and affected service name, create a P1 ServiceNow incident, post a Teams alert to the platform-engineering channel, and open a Jira bug. Use when automated alerting detects SLA-breaching errors."
          inputParameters:
            - name: alert_id
              in: body
              type: string
              description: "Datadog monitor alert ID that triggered this incident."
            - name: service_name
              in: body
              type: string
              description: "Affected service name (e.g., sciencedirect-content-api)."
            - name: severity
              in: body
              type: string
              description: "Incident severity: P1, P2, or P3."
          steps:
            - name: get-alert-details
              type: call
              call: datadog.get-monitor-alert
              with:
                alert_id: "{{alert_id}}"
            - name: create-incident
              type: call
              call: servicenow.create-incident
              with:
                short_description: "[{{severity}}] Content platform degradation: {{service_name}}"
                urgency: "1"
                impact: "1"
                description: "Datadog alert {{alert_id}}: {{get-alert-details.message}}"
            - name: create-bug
              type: call
              call: jira.create-issue
              with:
                project_key: "PLAT"
                issuetype: "Bug"
                summary: "[{{severity}}] {{service_name}} content delivery failure"
                description: "Incident: {{create-incident.number}} | Alert: {{alert_id}} | Details: {{get-alert-details.message}}"
            - name: notify-teams
              type: call
              call: msteams.send-channel-message
              with:
                channel: "platform-engineering"
                text: "INCIDENT {{create-incident.number}} | {{severity}} | {{service_name}} | Jira: {{create-bug.key}}"
  consumes:
    - type: http
      namespace: datadog
      baseUri: "https://api.datadoghq.com/api/v1"
      authentication:
        type: apikey
        key: "DD-API-KEY"
        value: "$secrets.datadog_api_key"
        placement: header
      resources:
        - name: monitor-alerts
          path: "/monitor/{{alert_id}}"
          inputParameters:
            - name: alert_id
              in: path
          operations:
            - name: get-monitor-alert
              method: GET
    - type: http
      namespace: servicenow
      baseUri: "https://elsevier.service-now.com/api/now"
      authentication:
        type: basic
        username: "$secrets.servicenow_user"
        password: "$secrets.servicenow_password"
      resources:
        - name: incidents
          path: "/table/incident"
          operations:
            - name: create-incident
              method: POST
    - type: http
      namespace: jira
      baseUri: "https://elsevier.atlassian.net/rest/api/3"
      authentication:
        type: basic
        username: "$secrets.jira_user"
        password: "$secrets.jira_api_token"
      resources:
        - name: issues
          path: "/issue"
          operations:
            - name: create-issue
              method: POST
    - type: http
      namespace: msteams
      baseUri: "https://graph.microsoft.com/v1.0"
      authentication:
        type: bearer
        token: "$secrets.msteams_token"
      resources:
        - name: channel-messages
          path: "/teams/platform/channels/general/messages"
          operations:
            - name: send-channel-message
              method: POST

Retrieves article metadata from Crossref by DOI, returning title, authors, journal, and publication date.

naftiko: "0.5"
info:
  label: "Crossref DOI Metadata Lookup"
  description: "Retrieves article metadata from Crossref by DOI, returning title, authors, journal, and publication date."
  tags:
    - publishing
    - metadata
    - crossref
capability:
  exposes:
    - type: mcp
      namespace: publishing-ops
      port: 8080
      tools:
        - name: get-doi-metadata
          description: "Given a DOI, return the article title, authors, journal name, and publication date from Crossref."
          inputParameters:
            - name: doi
              in: body
              type: string
              description: "Article DOI."
          call: crossref.get-work
          with:
            doi: "{{doi}}"
          outputParameters:
            - name: title
              type: string
              mapping: "$.message.title[0]"
            - name: journal
              type: string
              mapping: "$.message.container-title[0]"
  consumes:
    - type: http
      namespace: crossref
      baseUri: "https://api.crossref.org"
      authentication:
        type: none
      resources:
        - name: works
          path: "/works/{{doi}}"
          inputParameters:
            - name: doi
              in: path
          operations:
            - name: get-work
              method: GET

Retrieves the status of a Databricks content processing pipeline job run by run ID.

naftiko: "0.5"
info:
  label: "Databricks Content Pipeline Status"
  description: "Retrieves the status of a Databricks content processing pipeline job run by run ID."
  tags:
    - data-engineering
    - publishing
    - databricks
capability:
  exposes:
    - type: mcp
      namespace: data-ops
      port: 8080
      tools:
        - name: get-pipeline-status
          description: "Given a Databricks run ID, return the pipeline state, start time, and result."
          inputParameters:
            - name: run_id
              in: body
              type: string
              description: "Databricks job run ID."
          call: databricks.get-run
          with:
            run_id: "{{run_id}}"
          outputParameters:
            - name: state
              type: string
              mapping: "$.state.life_cycle_state"
            - name: result
              type: string
              mapping: "$.state.result_state"
  consumes:
    - type: http
      namespace: databricks
      baseUri: "https://elsevier.cloud.databricks.com/api/2.1"
      authentication:
        type: bearer
        token: "$secrets.databricks_token"
      resources:
        - name: runs
          path: "/jobs/runs/get"
          inputParameters:
            - name: run_id
              in: query
          operations:
            - name: get-run
              method: GET

Queries Datadog for platform service health metrics and posts a status summary to Microsoft Teams.

naftiko: "0.5"
info:
  label: "Datadog Platform Health Check"
  description: "Queries Datadog for platform service health metrics and posts a status summary to Microsoft Teams."
  tags:
    - monitoring
    - observability
    - datadog
    - microsoft-teams
capability:
  exposes:
    - type: mcp
      namespace: platform-ops
      port: 8080
      tools:
        - name: check-platform-health
          description: "Query Datadog for key platform metrics and post a health summary to the ops Teams channel."
          inputParameters:
            - name: service_name
              in: body
              type: string
              description: "Service name to check."
          steps:
            - name: query-health
              type: call
              call: datadog.query-timeseries
              with:
                query: "avg:trace.http.request.duration{service:{{service_name}}}.as_rate()"
            - name: post-status
              type: call
              call: msteams.post-channel-message
              with:
                channel_id: "platform-ops"
                text: "Health check for {{service_name}}: avg latency = {{query-health.series[0].pointlist[-1][1]}}ms"
  consumes:
    - type: http
      namespace: datadog
      baseUri: "https://api.datadoghq.com/api/v1"
      authentication:
        type: apikey
        key: "DD-API-KEY"
        value: "$secrets.datadog_api_key"
        placement: header
      resources:
        - name: query
          path: "/query"
          operations:
            - name: query-timeseries
              method: GET
    - type: http
      namespace: msteams
      baseUri: "https://graph.microsoft.com/v1.0"
      authentication:
        type: bearer
        token: "$secrets.msteams_token"
      resources:
        - name: channel-messages
          path: "/teams/{{team_id}}/channels/{{channel_id}}/messages"
          inputParameters:
            - name: channel_id
              in: path
          operations:
            - name: post-channel-message
              method: POST

Queries Datadog for all defined SLOs on content platform services, computes compliance rates, writes results to Snowflake, and alerts engineering leadership via Teams when SLOs are at risk.

naftiko: "0.5"
info:
  label: "Datadog SLO Compliance Report"
  description: "Queries Datadog for all defined SLOs on content platform services, computes compliance rates, writes results to Snowflake, and alerts engineering leadership via Teams when SLOs are at risk."
  tags:
    - observability
    - slo
    - datadog
    - snowflake
    - msteams
    - reporting
capability:
  exposes:
    - type: mcp
      namespace: slo-reporting
      port: 8080
      tools:
        - name: report-slo-compliance
          description: "Given a service tag and time window, fetch SLO status from Datadog, persist results to Snowflake, and post a Teams alert if any SLO is below 99.5%. Use weekly for service reliability reviews."
          inputParameters:
            - name: service_tag
              in: body
              type: string
              description: "Datadog service tag to filter SLOs (e.g., service:sciencedirect)."
            - name: time_window
              in: body
              type: string
              description: "SLO evaluation window: 7d, 30d, or 90d."
          steps:
            - name: get-slo-status
              type: call
              call: datadog.list-slos
              with:
                tags: "{{service_tag}}"
                time_window: "{{time_window}}"
            - name: write-slo-data
              type: call
              call: snowflake.insert-slo-row
              with:
                service_tag: "{{service_tag}}"
                time_window: "{{time_window}}"
                at_risk_count: "{{get-slo-status.at_risk_count}}"
            - name: alert-leadership
              type: call
              call: msteams.send-message
              with:
                channel: "engineering-leadership"
                text: "SLO Report ({{time_window}}): {{get-slo-status.at_risk_count}} SLOs at risk for {{service_tag}}."
  consumes:
    - type: http
      namespace: datadog
      baseUri: "https://api.datadoghq.com/api/v1"
      authentication:
        type: apikey
        key: "DD-API-KEY"
        value: "$secrets.datadog_api_key"
        placement: header
      resources:
        - name: slos
          path: "/slo"
          inputParameters:
            - name: tags
              in: query
            - name: time_window
              in: query
          operations:
            - name: list-slos
              method: GET
    - type: http
      namespace: snowflake
      baseUri: "https://elsevier.snowflakecomputing.com/api/v2"
      authentication:
        type: bearer
        token: "$secrets.snowflake_token"
      resources:
        - name: slo-table
          path: "/statements"
          operations:
            - name: insert-slo-row
              method: POST
    - type: http
      namespace: msteams
      baseUri: "https://graph.microsoft.com/v1.0"
      authentication:
        type: bearer
        token: "$secrets.msteams_token"
      resources:
        - name: channel-messages
          path: "/teams/engineering/channels/leadership/messages"
          operations:
            - name: send-message
              method: POST

Retrieves the status of a DocuSign publishing agreement envelope, returning signer status and completion date.

naftiko: "0.5"
info:
  label: "DocuSign Publishing Agreement Status"
  description: "Retrieves the status of a DocuSign publishing agreement envelope, returning signer status and completion date."
  tags:
    - legal
    - publishing
    - docusign
capability:
  exposes:
    - type: mcp
      namespace: legal-ops
      port: 8080
      tools:
        - name: get-agreement-status
          description: "Given a DocuSign envelope ID, return status and signer progress."
          inputParameters:
            - name: envelope_id
              in: body
              type: string
              description: "DocuSign envelope ID."
          call: docusign.get-envelope
          with:
            envelope_id: "{{envelope_id}}"
          outputParameters:
            - name: status
              type: string
              mapping: "$.status"
            - name: completed_date
              type: string
              mapping: "$.completedDateTime"
  consumes:
    - type: http
      namespace: docusign
      baseUri: "https://na4.docusign.net/restapi/v2.1/accounts/$secrets.docusign_account_id"
      authentication:
        type: bearer
        token: "$secrets.docusign_token"
      resources:
        - name: envelopes
          path: "/envelopes/{{envelope_id}}"
          inputParameters:
            - name: envelope_id
              in: path
          operations:
            - name: get-envelope
              method: GET

Searches the article index in Elasticsearch by keyword, author, or DOI.

naftiko: "0.5"
info:
  label: "Elasticsearch Article Search"
  description: "Searches the article index in Elasticsearch by keyword, author, or DOI."
  tags:
    - publishing
    - search
    - elasticsearch
capability:
  exposes:
    - type: mcp
      namespace: content-ops
      port: 8080
      tools:
        - name: search-articles
          description: "Given a search query, search the Elasticsearch article index."
          inputParameters:
            - name: query
              in: body
              type: string
              description: "Search query (keyword, author, or DOI)."
          call: elasticsearch.search
          with:
            index: "articles"
            q: "{{query}}"
          outputParameters:
            - name: hits
              type: array
              mapping: "$.hits.hits[*]._source"
            - name: total
              type: integer
              mapping: "$.hits.total.value"
  consumes:
    - type: http
      namespace: elasticsearch
      baseUri: "https://elsevier-es.eu-west-1.es.amazonaws.com"
      authentication:
        type: aws-sigv4
        accessKeyId: "$secrets.aws_access_key"
        secretAccessKey: "$secrets.aws_secret_key"
      resources:
        - name: search
          path: "/articles/_search"
          operations:
            - name: search
              method: POST

When an employee termination is processed in Workday, disables the Microsoft 365 account, revokes GitHub organization access, and opens a ServiceNow offboarding ticket.

naftiko: "0.5"
info:
  label: "Employee Offboarding"
  description: "When an employee termination is processed in Workday, disables the Microsoft 365 account, revokes GitHub organization access, and opens a ServiceNow offboarding ticket."
  tags:
    - hr
    - offboarding
    - workday
    - microsoft-365
    - github
    - servicenow
    - identity
capability:
  exposes:
    - type: mcp
      namespace: hr-offboarding
      port: 8080
      tools:
        - name: trigger-employee-offboarding
          description: "Given a Workday employee ID and termination date, disable the Microsoft 365 account, remove GitHub org membership, and create a ServiceNow offboarding ticket. Use on confirmed HR termination events."
          inputParameters:
            - name: workday_employee_id
              in: body
              type: string
              description: "Workday worker ID of the departing employee."
            - name: termination_date
              in: body
              type: string
              description: "Effective termination date in ISO 8601 format (YYYY-MM-DD)."
          steps:
            - name: get-worker
              type: call
              call: workday.get-worker
              with:
                worker_id: "{{workday_employee_id}}"
            - name: disable-m365-account
              type: call
              call: msgraph.disable-user
              with:
                user_id: "{{get-worker.email}}"
                accountEnabled: "false"
            - name: remove-github-membership
              type: call
              call: github.remove-org-member
              with:
                org: "elsevier"
                username: "{{get-worker.github_username}}"
            - name: open-offboarding-ticket
              type: call
              call: servicenow.create-incident
              with:
                short_description: "Offboarding: {{get-worker.full_name}} — {{termination_date}}"
                category: "offboarding"
                assignment_group: "IT_Security"
  consumes:
    - type: http
      namespace: workday
      baseUri: "https://wd2-impl-services1.workday.com/ccx/api/v1"
      authentication:
        type: bearer
        token: "$secrets.workday_token"
      resources:
        - name: workers
          path: "/workers/{{worker_id}}"
          inputParameters:
            - name: worker_id
              in: path
          operations:
            - name: get-worker
              method: GET
    - type: http
      namespace: msgraph
      baseUri: "https://graph.microsoft.com/v1.0"
      authentication:
        type: bearer
        token: "$secrets.msgraph_token"
      resources:
        - name: users
          path: "/users/{{user_id}}"
          inputParameters:
            - name: user_id
              in: path
          operations:
            - name: disable-user
              method: PATCH
    - type: http
      namespace: github
      baseUri: "https://api.github.com"
      authentication:
        type: bearer
        token: "$secrets.github_token"
      resources:
        - name: org-members
          path: "/orgs/{{org}}/members/{{username}}"
          inputParameters:
            - name: org
              in: path
            - name: username
              in: path
          operations:
            - name: remove-org-member
              method: DELETE
    - type: http
      namespace: servicenow
      baseUri: "https://elsevier.service-now.com/api/now"
      authentication:
        type: basic
        username: "$secrets.servicenow_user"
        password: "$secrets.servicenow_password"
      resources:
        - name: incidents
          path: "/table/incident"
          operations:
            - name: create-incident
              method: POST

When a journal cover design is updated in Figma, posts a review request to the editorial Microsoft Teams channel.

naftiko: "0.5"
info:
  label: "Figma Cover Design Review"
  description: "When a journal cover design is updated in Figma, posts a review request to the editorial Microsoft Teams channel."
  tags:
    - design
    - publishing
    - figma
    - microsoft-teams
capability:
  exposes:
    - type: mcp
      namespace: design-ops
      port: 8080
      tools:
        - name: request-cover-review
          description: "Given a Figma file key, fetch file info and post a review request to Teams."
          inputParameters:
            - name: file_key
              in: body
              type: string
              description: "Figma file key."
          steps:
            - name: get-file
              type: call
              call: figma.get-file
              with:
                file_key: "{{file_key}}"
            - name: post-review
              type: call
              call: msteams.post-channel-message
              with:
                channel_id: "editorial-design"
                text: "Cover design review: {{get-file.name}} — https://www.figma.com/file/{{file_key}}"
  consumes:
    - type: http
      namespace: figma
      baseUri: "https://api.figma.com/v1"
      authentication:
        type: bearer
        token: "$secrets.figma_token"
      resources:
        - name: files
          path: "/files/{{file_key}}"
          inputParameters:
            - name: file_key
              in: path
          operations:
            - name: get-file
              method: GET
    - type: http
      namespace: msteams
      baseUri: "https://graph.microsoft.com/v1.0"
      authentication:
        type: bearer
        token: "$secrets.msteams_token"
      resources:
        - name: channel-messages
          path: "/teams/{{team_id}}/channels/{{channel_id}}/messages"
          inputParameters:
            - name: channel_id
              in: path
          operations:
            - name: post-channel-message
              method: POST

Triggers a GitHub Actions workflow and notifies the engineering Slack channel.

naftiko: "0.5"
info:
  label: "GitHub Actions Pipeline Trigger"
  description: "Triggers a GitHub Actions workflow and notifies the engineering Slack channel."
  tags:
    - engineering
    - cicd
    - github-actions
    - slack
capability:
  exposes:
    - type: mcp
      namespace: cicd-ops
      port: 8080
      tools:
        - name: trigger-workflow
          description: "Given a repo and workflow file, trigger a workflow dispatch and notify Slack."
          inputParameters:
            - name: repo
              in: body
              type: string
              description: "GitHub repo in org/repo format."
            - name: workflow_id
              in: body
              type: string
              description: "Workflow file name."
            - name: ref
              in: body
              type: string
              description: "Git ref."
          steps:
            - name: dispatch
              type: call
              call: github.create-workflow-dispatch
              with:
                repo: "{{repo}}"
                workflow_id: "{{workflow_id}}"
                ref: "{{ref}}"
            - name: notify
              type: call
              call: slack.post-message
              with:
                channel: "engineering"
                text: "Workflow {{workflow_id}} triggered on {{repo}} ({{ref}})"
  consumes:
    - type: http
      namespace: github
      baseUri: "https://api.github.com"
      authentication:
        type: bearer
        token: "$secrets.github_token"
      resources:
        - name: dispatches
          path: "/repos/{{repo}}/actions/workflows/{{workflow_id}}/dispatches"
          inputParameters:
            - name: repo
              in: path
            - name: workflow_id
              in: path
          operations:
            - name: create-workflow-dispatch
              method: POST
    - type: http
      namespace: slack
      baseUri: "https://slack.com/api"
      authentication:
        type: bearer
        token: "$secrets.slack_bot_token"
      resources:
        - name: messages
          path: "/chat.postMessage"
          operations:
            - name: post-message
              method: POST

Checks whether a GitHub repository meets code review policies including branch protection and CODEOWNERS, posting results to Slack.

naftiko: "0.5"
info:
  label: "GitHub Code Review Policy Check"
  description: "Checks whether a GitHub repository meets code review policies including branch protection and CODEOWNERS, posting results to Slack."
  tags:
    - engineering
    - compliance
    - github
    - slack
capability:
  exposes:
    - type: mcp
      namespace: eng-compliance
      port: 8080
      tools:
        - name: check-review-policy
          description: "Given a GitHub repository, verify branch protection and CODEOWNERS presence, then post compliance status to Slack."
          inputParameters:
            - name: repo
              in: body
              type: string
              description: "GitHub repository in org/repo format."
          steps:
            - name: get-protection
              type: call
              call: github.get-branch-protection
              with:
                repo: "{{repo}}"
                branch: "main"
            - name: post-status
              type: call
              call: slack.post-message
              with:
                channel: "engineering-compliance"
                text: "Repo compliance check for {{repo}}: branch protection={{get-protection.enabled}}"
  consumes:
    - type: http
      namespace: github
      baseUri: "https://api.github.com"
      authentication:
        type: bearer
        token: "$secrets.github_token"
      resources:
        - name: branch-protection
          path: "/repos/{{repo}}/branches/{{branch}}/protection"
          inputParameters:
            - name: repo
              in: path
            - name: branch
              in: path
          operations:
            - name: get-branch-protection
              method: GET
    - type: http
      namespace: slack
      baseUri: "https://slack.com/api"
      authentication:
        type: bearer
        token: "$secrets.slack_bot_token"
      resources:
        - name: messages
          path: "/chat.postMessage"
          operations:
            - name: post-message
              method: POST

On a pull request to a main branch in GitHub, triggers a code quality scan, posts results as a PR comment, and blocks merge if quality thresholds are not met.

naftiko: "0.5"
info:
  label: "GitHub Pull Request Quality Gate"
  description: "On a pull request to a main branch in GitHub, triggers a code quality scan, posts results as a PR comment, and blocks merge if quality thresholds are not met."
  tags:
    - devops
    - github
    - code-quality
    - ci-cd
capability:
  exposes:
    - type: mcp
      namespace: devops-quality
      port: 8080
      tools:
        - name: run-pr-quality-gate
          description: "Given a GitHub repository name, PR number, and commit SHA, fetch PR metadata, post a status check result, and add a summary comment. Use as part of the CI/CD quality enforcement pipeline."
          inputParameters:
            - name: repo_full_name
              in: body
              type: string
              description: "GitHub repository full name (e.g., elsevier/sciencedirect-api)."
            - name: pr_number
              in: body
              type: integer
              description: "Pull request number."
            - name: commit_sha
              in: body
              type: string
              description: "Head commit SHA for the pull request."
            - name: quality_score
              in: body
              type: number
              description: "Code quality score from 0 to 100 from the upstream scanner."
          steps:
            - name: get-pr
              type: call
              call: github.get-pull-request
              with:
                repo: "{{repo_full_name}}"
                pull_number: "{{pr_number}}"
            - name: post-status
              type: call
              call: github-status.create-commit-status
              with:
                repo: "{{repo_full_name}}"
                sha: "{{commit_sha}}"
                state: "success"
                description: "Quality score: {{quality_score}}/100"
                context: "naftiko/quality-gate"
            - name: post-comment
              type: call
              call: github-comment.create-issue-comment
              with:
                repo: "{{repo_full_name}}"
                issue_number: "{{pr_number}}"
                body: "Quality Gate Result: {{quality_score}}/100 — {{get-pr.title}}"
  consumes:
    - type: http
      namespace: github
      baseUri: "https://api.github.com"
      authentication:
        type: bearer
        token: "$secrets.github_token"
      resources:
        - name: pull-requests
          path: "/repos/{{repo}}/pulls/{{pull_number}}"
          inputParameters:
            - name: repo
              in: path
            - name: pull_number
              in: path
          operations:
            - name: get-pull-request
              method: GET
    - type: http
      namespace: github-status
      baseUri: "https://api.github.com"
      authentication:
        type: bearer
        token: "$secrets.github_token"
      resources:
        - name: commit-statuses
          path: "/repos/{{repo}}/statuses/{{sha}}"
          inputParameters:
            - name: repo
              in: path
            - name: sha
              in: path
          operations:
            - name: create-commit-status
              method: POST
    - type: http
      namespace: github-comment
      baseUri: "https://api.github.com"
      authentication:
        type: bearer
        token: "$secrets.github_token"
      resources:
        - name: issue-comments
          path: "/repos/{{repo}}/issues/{{issue_number}}/comments"
          inputParameters:
            - name: repo
              in: path
            - name: issue_number
              in: path
          operations:
            - name: create-issue-comment
              method: POST

Fetches open Dependabot alerts for a repository and posts the severity summary to the security Microsoft Teams channel.

naftiko: "0.5"
info:
  label: "GitHub Security Vulnerability Scan"
  description: "Fetches open Dependabot alerts for a repository and posts the severity summary to the security Microsoft Teams channel."
  tags:
    - security
    - engineering
    - github
    - microsoft-teams
capability:
  exposes:
    - type: mcp
      namespace: appsec
      port: 8080
      tools:
        - name: scan-vulnerabilities
          description: "Given a GitHub repo, fetch open security alerts and post a summary to the security Teams channel."
          inputParameters:
            - name: repo
              in: body
              type: string
              description: "GitHub repo in org/repo format."
          steps:
            - name: get-alerts
              type: call
              call: github.list-dependabot-alerts
              with:
                repo: "{{repo}}"
                state: "open"
            - name: post-summary
              type: call
              call: msteams.post-channel-message
              with:
                channel_id: "security-alerts"
                text: "Security scan for {{repo}}: {{get-alerts.total}} open vulnerabilities"
  consumes:
    - type: http
      namespace: github
      baseUri: "https://api.github.com"
      authentication:
        type: bearer
        token: "$secrets.github_token"
      resources:
        - name: dependabot-alerts
          path: "/repos/{{repo}}/dependabot/alerts"
          inputParameters:
            - name: repo
              in: path
          operations:
            - name: list-dependabot-alerts
              method: GET
    - type: http
      namespace: msteams
      baseUri: "https://graph.microsoft.com/v1.0"
      authentication:
        type: bearer
        token: "$secrets.msteams_token"
      resources:
        - name: channel-messages
          path: "/teams/{{team_id}}/channels/{{channel_id}}/messages"
          inputParameters:
            - name: channel_id
              in: path
          operations:
            - name: post-channel-message
              method: POST

Scans GitHub Dependabot alerts for critical vulnerabilities across Elsevier repositories, creates Jira security issues for each, and notifies the security team via Teams.

naftiko: "0.5"
info:
  label: "GitHub Security Vulnerability Triage"
  description: "Scans GitHub Dependabot alerts for critical vulnerabilities across Elsevier repositories, creates Jira security issues for each, and notifies the security team via Teams."
  tags:
    - security
    - devops
    - github
    - jira
    - msteams
    - vulnerability-management
capability:
  exposes:
    - type: mcp
      namespace: security-triage
      port: 8080
      tools:
        - name: triage-dependabot-alerts
          description: "Given a GitHub organization name and severity filter, list open Dependabot vulnerability alerts, create a Jira security issue for each critical finding, and post a summary to the security Teams channel."
          inputParameters:
            - name: github_org
              in: body
              type: string
              description: "GitHub organization name to scan (e.g., elsevier)."
            - name: severity
              in: body
              type: string
              description: "Minimum severity to triage: critical, high, medium, or low."
          steps:
            - name: list-alerts
              type: call
              call: github.list-org-dependabot-alerts
              with:
                org: "{{github_org}}"
                severity: "{{severity}}"
            - name: create-security-issue
              type: call
              call: jira.create-issue
              with:
                project_key: "SEC"
                issuetype: "Security Vulnerability"
                summary: "[{{severity}}] Dependabot alerts in {{github_org}}: {{list-alerts.alert_count}} findings"
                description: "Repositories affected: {{list-alerts.repo_names}}. Packages: {{list-alerts.package_names}}"
            - name: notify-security-team
              type: call
              call: msteams.send-message
              with:
                channel: "security-ops"
                text: "Dependabot triage: {{list-alerts.alert_count}} {{severity}} alerts in {{github_org}}. Jira: {{create-security-issue.key}}"
  consumes:
    - type: http
      namespace: github
      baseUri: "https://api.github.com"
      authentication:
        type: bearer
        token: "$secrets.github_token"
      resources:
        - name: dependabot-alerts
          path: "/orgs/{{org}}/dependabot/alerts"
          inputParameters:
            - name: org
              in: path
            - name: severity
              in: query
          operations:
            - name: list-org-dependabot-alerts
              method: GET
    - type: http
      namespace: jira
      baseUri: "https://elsevier.atlassian.net/rest/api/3"
      authentication:
        type: basic
        username: "$secrets.jira_user"
        password: "$secrets.jira_api_token"
      resources:
        - name: issues
          path: "/issue"
          operations:
            - name: create-issue
              method: POST
    - type: http
      namespace: msteams
      baseUri: "https://graph.microsoft.com/v1.0"
      authentication:
        type: bearer
        token: "$secrets.msteams_token"
      resources:
        - name: channel-messages
          path: "/teams/security/channels/ops/messages"
          operations:
            - name: send-message
              method: POST

Retrieves weekly content usage metrics from Google Analytics for a property and posts to Microsoft Teams.

naftiko: "0.5"
info:
  label: "Google Analytics Content Usage"
  description: "Retrieves weekly content usage metrics from Google Analytics for a property and posts to Microsoft Teams."
  tags:
    - analytics
    - publishing
    - google-analytics
    - microsoft-teams
capability:
  exposes:
    - type: mcp
      namespace: content-analytics
      port: 8080
      tools:
        - name: post-usage-report
          description: "Fetch weekly usage metrics from Google Analytics and post to Teams."
          inputParameters:
            - name: property_id
              in: body
              type: string
              description: "Google Analytics property ID."
          steps:
            - name: get-report
              type: call
              call: ga.run-report
              with:
                property_id: "{{property_id}}"
                date_range: "last7days"
            - name: post-teams
              type: call
              call: msteams.post-channel-message
              with:
                channel_id: "content-analytics"
                text: "Weekly content usage: {{get-report.sessions}} sessions, {{get-report.users}} users"
  consumes:
    - type: http
      namespace: ga
      baseUri: "https://analyticsdata.googleapis.com/v1beta"
      authentication:
        type: bearer
        token: "$secrets.google_analytics_token"
      resources:
        - name: reports
          path: "/properties/{{property_id}}:runReport"
          inputParameters:
            - name: property_id
              in: path
          operations:
            - name: run-report
              method: POST
    - type: http
      namespace: msteams
      baseUri: "https://graph.microsoft.com/v1.0"
      authentication:
        type: bearer
        token: "$secrets.msteams_token"
      resources:
        - name: channel-messages
          path: "/teams/{{team_id}}/channels/{{channel_id}}/messages"
          inputParameters:
            - name: channel_id
              in: path
          operations:
            - name: post-channel-message
              method: POST

Uploads an editorial file to Google Drive and shares the link with the author via email.

naftiko: "0.5"
info:
  label: "Google Drive Editorial File Share"
  description: "Uploads an editorial file to Google Drive and shares the link with the author via email."
  tags:
    - publishing
    - collaboration
    - google-drive
    - sendgrid
capability:
  exposes:
    - type: mcp
      namespace: editorial-ops
      port: 8080
      tools:
        - name: share-editorial-file
          description: "Upload a file to Google Drive and email the share link to the author."
          inputParameters:
            - name: file_name
              in: body
              type: string
              description: "File name."
            - name: folder_id
              in: body
              type: string
              description: "Google Drive folder ID."
            - name: author_email
              in: body
              type: string
              description: "Author email."
          steps:
            - name: upload-file
              type: call
              call: gdrive.upload-file
              with:
                folder_id: "{{folder_id}}"
                name: "{{file_name}}"
            - name: email-author
              type: call
              call: sendgrid.send-email
              with:
                to: "{{author_email}}"
                subject: "Your editorial file is ready"
                content: "Download your file: {{upload-file.webViewLink}}"
  consumes:
    - type: http
      namespace: gdrive
      baseUri: "https://www.googleapis.com/upload/drive/v3"
      authentication:
        type: bearer
        token: "$secrets.google_drive_token"
      resources:
        - name: files
          path: "/files"
          operations:
            - name: upload-file
              method: POST
    - type: http
      namespace: sendgrid
      baseUri: "https://api.sendgrid.com/v3"
      authentication:
        type: bearer
        token: "$secrets.sendgrid_api_key"
      resources:
        - name: mail
          path: "/mail/send"
          operations:
            - name: send-email
              method: POST

Retrieves the current state of a Grafana alert rule monitoring content delivery performance.

naftiko: "0.5"
info:
  label: "Grafana Content Delivery Dashboard"
  description: "Retrieves the current state of a Grafana alert rule monitoring content delivery performance."
  tags:
    - monitoring
    - publishing
    - grafana
capability:
  exposes:
    - type: mcp
      namespace: observability
      port: 8080
      tools:
        - name: get-delivery-alert-status
          description: "Given a Grafana alert rule UID, return its current firing state."
          inputParameters:
            - name: rule_uid
              in: body
              type: string
              description: "Grafana alert rule UID."
          call: grafana.get-alert-rule
          with:
            uid: "{{rule_uid}}"
          outputParameters:
            - name: state
              type: string
              mapping: "$.state"
  consumes:
    - type: http
      namespace: grafana
      baseUri: "https://grafana.elsevier.com/api"
      authentication:
        type: bearer
        token: "$secrets.grafana_token"
      resources:
        - name: alert-rules
          path: "/v1/provisioning/alert-rules/{{uid}}"
          inputParameters:
            - name: uid
              in: path
          operations:
            - name: get-alert-rule
              method: GET

Fetches author data from Salesforce and upserts into HubSpot for marketing engagement.

naftiko: "0.5"
info:
  label: "HubSpot Author Contact Sync"
  description: "Fetches author data from Salesforce and upserts into HubSpot for marketing engagement."
  tags:
    - marketing
    - crm
    - hubspot
    - salesforce
capability:
  exposes:
    - type: mcp
      namespace: marketing-ops
      port: 8080
      tools:
        - name: sync-author-to-hubspot
          description: "Given a Salesforce contact ID, fetch author data and upsert into HubSpot."
          inputParameters:
            - name: contact_id
              in: body
              type: string
              description: "Salesforce contact ID."
          steps:
            - name: get-sf-contact
              type: call
              call: salesforce.get-contact
              with:
                contact_id: "{{contact_id}}"
            - name: upsert-hubspot
              type: call
              call: hubspot.upsert-contact
              with:
                email: "{{get-sf-contact.Email}}"
                properties:
                  firstname: "{{get-sf-contact.FirstName}}"
                  lastname: "{{get-sf-contact.LastName}}"
  consumes:
    - type: http
      namespace: salesforce
      baseUri: "https://elsevier.my.salesforce.com/services/data/v58.0"
      authentication:
        type: bearer
        token: "$secrets.salesforce_token"
      resources:
        - name: contacts
          path: "/sobjects/Contact/{{contact_id}}"
          inputParameters:
            - name: contact_id
              in: path
          operations:
            - name: get-contact
              method: GET
    - type: http
      namespace: hubspot
      baseUri: "https://api.hubapi.com"
      authentication:
        type: bearer
        token: "$secrets.hubspot_token"
      resources:
        - name: contacts
          path: "/crm/v3/objects/contacts"
          operations:
            - name: upsert-contact
              method: POST

Fetches HubSpot email campaign performance metrics, writes them to Snowflake, and triggers a Power BI refresh for the marketing leadership dashboard.

naftiko: "0.5"
info:
  label: "HubSpot Campaign Performance Report"
  description: "Fetches HubSpot email campaign performance metrics, writes them to Snowflake, and triggers a Power BI refresh for the marketing leadership dashboard."
  tags:
    - marketing
    - reporting
    - hubspot
    - snowflake
    - power-bi
capability:
  exposes:
    - type: mcp
      namespace: campaign-reporting
      port: 8080
      tools:
        - name: report-campaign-performance
          description: "Given a HubSpot campaign ID, fetch email open, click, and unsubscribe rates, write the metrics to Snowflake, and trigger a Power BI marketing dashboard refresh. Use after campaign completion."
          inputParameters:
            - name: hubspot_campaign_id
              in: body
              type: string
              description: "HubSpot email campaign ID to report on."
          steps:
            - name: get-campaign-stats
              type: call
              call: hubspot.get-campaign-stats
              with:
                campaign_id: "{{hubspot_campaign_id}}"
            - name: write-metrics
              type: call
              call: snowflake.insert-campaign-row
              with:
                campaign_id: "{{hubspot_campaign_id}}"
                open_rate: "{{get-campaign-stats.open_rate}}"
                click_rate: "{{get-campaign-stats.click_rate}}"
                unsubscribe_rate: "{{get-campaign-stats.unsubscribe_rate}}"
            - name: refresh-dashboard
              type: call
              call: powerbi.trigger-refresh
              with:
                dataset_id: "$secrets.powerbi_marketing_dataset_id"
  consumes:
    - type: http
      namespace: hubspot
      baseUri: "https://api.hubapi.com/marketing/v3"
      authentication:
        type: bearer
        token: "$secrets.hubspot_token"
      resources:
        - name: campaign-stats
          path: "/emails/statistics/list"
          inputParameters:
            - name: campaign_id
              in: query
          operations:
            - name: get-campaign-stats
              method: GET
    - type: http
      namespace: snowflake
      baseUri: "https://elsevier.snowflakecomputing.com/api/v2"
      authentication:
        type: bearer
        token: "$secrets.snowflake_token"
      resources:
        - name: campaign-metrics
          path: "/statements"
          operations:
            - name: insert-campaign-row
              method: POST
    - type: http
      namespace: powerbi
      baseUri: "https://api.powerbi.com/v1.0/myorg"
      authentication:
        type: bearer
        token: "$secrets.powerbi_token"
      resources:
        - name: dataset-refreshes
          path: "/datasets/{{dataset_id}}/refreshes"
          inputParameters:
            - name: dataset_id
              in: path
          operations:
            - name: trigger-refresh
              method: POST

When a new HubSpot contact is created from a trial form, enriches the record with Scopus publication count and research area, then updates the contact properties.

naftiko: "0.5"
info:
  label: "HubSpot Lead Enrichment from Scopus"
  description: "When a new HubSpot contact is created from a trial form, enriches the record with Scopus publication count and research area, then updates the contact properties."
  tags:
    - marketing
    - crm
    - data-enrichment
    - hubspot
    - scopus
capability:
  exposes:
    - type: mcp
      namespace: marketing-enrichment
      port: 8080
      tools:
        - name: enrich-hubspot-lead
          description: "Given a HubSpot contact ID and the contact's email, search Scopus for their author profile, then update the HubSpot contact with publication count and primary subject area. Use for trial lead scoring."
          inputParameters:
            - name: hubspot_contact_id
              in: body
              type: string
              description: "HubSpot contact ID for the trial signup."
            - name: author_email
              in: body
              type: string
              description: "Author email address to search in Scopus."
          steps:
            - name: search-scopus-author
              type: call
              call: scopus.search-author-by-email
              with:
                email: "{{author_email}}"
            - name: update-contact
              type: call
              call: hubspot.patch-contact
              with:
                contact_id: "{{hubspot_contact_id}}"
                publication_count: "{{search-scopus-author.document_count}}"
                primary_subject_area: "{{search-scopus-author.subject_area}}"
                h_index: "{{search-scopus-author.h_index}}"
  consumes:
    - type: http
      namespace: scopus
      baseUri: "https://api.elsevier.com/content/search/author"
      authentication:
        type: apikey
        key: "X-ELS-APIKey"
        value: "$secrets.scopus_api_key"
        placement: header
      resources:
        - name: author-search
          path: "/"
          inputParameters:
            - name: email
              in: query
          operations:
            - name: search-author-by-email
              method: GET
    - type: http
      namespace: hubspot
      baseUri: "https://api.hubapi.com/crm/v3"
      authentication:
        type: bearer
        token: "$secrets.hubspot_token"
      resources:
        - name: contacts
          path: "/objects/contacts/{{contact_id}}"
          inputParameters:
            - name: contact_id
              in: path
          operations:
            - name: patch-contact
              method: PATCH

Creates a Jira ticket in the editorial workflow project and notifies the editorial team via Microsoft Teams.

naftiko: "0.5"
info:
  label: "Jira Editorial Ticket Creation"
  description: "Creates a Jira ticket in the editorial workflow project and notifies the editorial team via Microsoft Teams."
  tags:
    - publishing
    - editorial
    - jira
    - microsoft-teams
capability:
  exposes:
    - type: mcp
      namespace: editorial-ops
      port: 8080
      tools:
        - name: create-editorial-ticket
          description: "Given a manuscript ID and issue type, create a Jira editorial ticket and notify the Teams editorial channel."
          inputParameters:
            - name: manuscript_id
              in: body
              type: string
              description: "Manuscript submission ID."
            - name: issue_type
              in: body
              type: string
              description: "Issue type (Revision, Proof, Correction)."
            - name: summary
              in: body
              type: string
              description: "Ticket summary."
          steps:
            - name: create-ticket
              type: call
              call: jira.create-issue
              with:
                project_key: "EDIT"
                issuetype: "{{issue_type}}"
                summary: "{{summary}}"
                description: "Manuscript: {{manuscript_id}}"
            - name: notify-editorial
              type: call
              call: msteams.post-channel-message
              with:
                channel_id: "editorial-workflow"
                text: "Editorial ticket created: {{create-ticket.key}} — {{summary}} (Manuscript: {{manuscript_id}})"
  consumes:
    - type: http
      namespace: jira
      baseUri: "https://elsevier.atlassian.net/rest/api/3"
      authentication:
        type: basic
        username: "$secrets.jira_user"
        password: "$secrets.jira_token"
      resources:
        - name: issues
          path: "/issue"
          operations:
            - name: create-issue
              method: POST
    - type: http
      namespace: msteams
      baseUri: "https://graph.microsoft.com/v1.0"
      authentication:
        type: bearer
        token: "$secrets.msteams_token"
      resources:
        - name: channel-messages
          path: "/teams/{{team_id}}/channels/{{channel_id}}/messages"
          inputParameters:
            - name: channel_id
              in: path
          operations:
            - name: post-channel-message
              method: POST

Searches Jira for overdue tickets and posts the count to a Microsoft Teams channel.

naftiko: "0.5"
info:
  label: "Jira Overdue Ticket Report"
  description: "Searches Jira for overdue tickets and posts the count to a Microsoft Teams channel."
  tags:
    - project-management
    - engineering
    - jira
    - microsoft-teams
capability:
  exposes:
    - type: mcp
      namespace: eng-ops
      port: 8080
      tools:
        - name: report-overdue-tickets
          description: "Search Jira for overdue tickets in a project and post the count to Teams."
          inputParameters:
            - name: project_key
              in: body
              type: string
              description: "Jira project key."
          steps:
            - name: search-overdue
              type: call
              call: jira.search-issues
              with:
                jql: "project={{project_key}} AND duedate < now() AND status != Done"
            - name: post-report
              type: call
              call: msteams.post-channel-message
              with:
                channel_id: "engineering"
                text: "Overdue tickets in {{project_key}}: {{search-overdue.total}} issues"
  consumes:
    - type: http
      namespace: jira
      baseUri: "https://elsevier.atlassian.net/rest/api/3"
      authentication:
        type: basic
        username: "$secrets.jira_user"
        password: "$secrets.jira_token"
      resources:
        - name: search
          path: "/search"
          operations:
            - name: search-issues
              method: POST
    - type: http
      namespace: msteams
      baseUri: "https://graph.microsoft.com/v1.0"
      authentication:
        type: bearer
        token: "$secrets.msteams_token"
      resources:
        - name: channel-messages
          path: "/teams/{{team_id}}/channels/{{channel_id}}/messages"
          inputParameters:
            - name: channel_id
              in: path
          operations:
            - name: post-channel-message
              method: POST

Fetches resolved Jira issues for a version and creates a Confluence release notes page.

naftiko: "0.5"
info:
  label: "Jira Release Notes Creation"
  description: "Fetches resolved Jira issues for a version and creates a Confluence release notes page."
  tags:
    - engineering
    - release-management
    - jira
    - confluence
capability:
  exposes:
    - type: mcp
      namespace: release-ops
      port: 8080
      tools:
        - name: create-release-notes
          description: "Given a Jira project and version, create Confluence release notes."
          inputParameters:
            - name: project_key
              in: body
              type: string
              description: "Jira project key."
            - name: version
              in: body
              type: string
              description: "Fix version name."
          steps:
            - name: get-issues
              type: call
              call: jira.search-issues
              with:
                jql: "project={{project_key}} AND fixVersion='{{version}}' AND status=Done"
            - name: create-page
              type: call
              call: confluence.create-content
              with:
                space_key: "RELEASES"
                title: "Release Notes — {{version}}"
                body: "{{get-issues.formatted}}"
  consumes:
    - type: http
      namespace: jira
      baseUri: "https://elsevier.atlassian.net/rest/api/3"
      authentication:
        type: basic
        username: "$secrets.jira_user"
        password: "$secrets.jira_token"
      resources:
        - name: search
          path: "/search"
          operations:
            - name: search-issues
              method: POST
    - type: http
      namespace: confluence
      baseUri: "https://elsevier.atlassian.net/wiki/rest/api"
      authentication:
        type: basic
        username: "$secrets.confluence_user"
        password: "$secrets.confluence_token"
      resources:
        - name: content
          path: "/content"
          operations:
            - name: create-content
              method: POST

Fetches completed sprint data from Jira, calculates velocity metrics, writes results to Snowflake, and publishes a Power BI report refresh for engineering leadership.

naftiko: "0.5"
info:
  label: "Jira Sprint Velocity Report"
  description: "Fetches completed sprint data from Jira, calculates velocity metrics, writes results to Snowflake, and publishes a Power BI report refresh for engineering leadership."
  tags:
    - devops
    - reporting
    - jira
    - snowflake
    - power-bi
    - agile
capability:
  exposes:
    - type: mcp
      namespace: engineering-reporting
      port: 8080
      tools:
        - name: publish-sprint-velocity
          description: "Given a Jira project key and sprint ID, fetch story points completed, write the velocity data to Snowflake, and trigger a Power BI refresh. Use after each sprint retrospective."
          inputParameters:
            - name: jira_project_key
              in: body
              type: string
              description: "Jira project key (e.g., PLAT, API)."
            - name: sprint_id
              in: body
              type: integer
              description: "Jira sprint ID to report on."
          steps:
            - name: get-sprint-report
              type: call
              call: jira.get-sprint
              with:
                sprint_id: "{{sprint_id}}"
            - name: write-velocity
              type: call
              call: snowflake.insert-velocity-row
              with:
                project_key: "{{jira_project_key}}"
                sprint_id: "{{sprint_id}}"
                completed_points: "{{get-sprint-report.completedPoints}}"
            - name: refresh-report
              type: call
              call: powerbi.trigger-refresh
              with:
                dataset_id: "$secrets.powerbi_velocity_dataset_id"
  consumes:
    - type: http
      namespace: jira
      baseUri: "https://elsevier.atlassian.net/rest/api/3"
      authentication:
        type: basic
        username: "$secrets.jira_user"
        password: "$secrets.jira_api_token"
      resources:
        - name: sprints
          path: "/sprint/{{sprint_id}}"
          inputParameters:
            - name: sprint_id
              in: path
          operations:
            - name: get-sprint
              method: GET
    - type: http
      namespace: snowflake
      baseUri: "https://elsevier.snowflakecomputing.com/api/v2"
      authentication:
        type: bearer
        token: "$secrets.snowflake_token"
      resources:
        - name: velocity-table
          path: "/statements"
          operations:
            - name: insert-velocity-row
              method: POST
    - type: http
      namespace: powerbi
      baseUri: "https://api.powerbi.com/v1.0/myorg"
      authentication:
        type: bearer
        token: "$secrets.powerbi_token"
      resources:
        - name: dataset-refreshes
          path: "/datasets/{{dataset_id}}/refreshes"
          inputParameters:
            - name: dataset_id
              in: path
          operations:
            - name: trigger-refresh
              method: POST

Pulls citation data from Scopus and article download metrics from the content platform, aggregates them in Snowflake, and publishes a Power BI dashboard refresh for editorial leadership.

naftiko: "0.5"
info:
  label: "Journal Impact Factor Report"
  description: "Pulls citation data from Scopus and article download metrics from the content platform, aggregates them in Snowflake, and publishes a Power BI dashboard refresh for editorial leadership."
  tags:
    - publishing
    - analytics
    - reporting
    - scopus
    - snowflake
    - power-bi
capability:
  exposes:
    - type: mcp
      namespace: journal-reporting
      port: 8080
      tools:
        - name: refresh-impact-report
          description: "Given a journal ISSN and reporting year, fetch citation counts from Scopus, write a summary row to Snowflake, and trigger a Power BI dataset refresh. Use for monthly editorial KPI reporting."
          inputParameters:
            - name: journal_issn
              in: body
              type: string
              description: "Journal ISSN (e.g., 0140-6736)."
            - name: report_year
              in: body
              type: integer
              description: "Four-digit reporting year (e.g., 2025)."
          steps:
            - name: get-citations
              type: call
              call: scopus.get-journal-citations
              with:
                issn: "{{journal_issn}}"
                year: "{{report_year}}"
            - name: write-snowflake
              type: call
              call: snowflake.insert-impact-row
              with:
                issn: "{{journal_issn}}"
                year: "{{report_year}}"
                citation_count: "{{get-citations.citation_count}}"
                impact_factor: "{{get-citations.impact_factor}}"
            - name: refresh-dataset
              type: call
              call: powerbi.trigger-refresh
              with:
                dataset_id: "$secrets.powerbi_journal_dataset_id"
  consumes:
    - type: http
      namespace: scopus
      baseUri: "https://api.elsevier.com/content/serial/title"
      authentication:
        type: apikey
        key: "X-ELS-APIKey"
        value: "$secrets.scopus_api_key"
        placement: header
      resources:
        - name: journal-metrics
          path: "/issn/{{issn}}"
          inputParameters:
            - name: issn
              in: path
            - name: year
              in: query
          operations:
            - name: get-journal-citations
              method: GET
    - type: http
      namespace: snowflake
      baseUri: "https://elsevier.snowflakecomputing.com/api/v2"
      authentication:
        type: bearer
        token: "$secrets.snowflake_token"
      resources:
        - name: impact-table
          path: "/statements"
          operations:
            - name: insert-impact-row
              method: POST
    - type: http
      namespace: powerbi
      baseUri: "https://api.powerbi.com/v1.0/myorg"
      authentication:
        type: bearer
        token: "$secrets.powerbi_token"
      resources:
        - name: dataset-refresh
          path: "/datasets/{{dataset_id}}/refreshes"
          inputParameters:
            - name: dataset_id
              in: path
          operations:
            - name: trigger-refresh
              method: POST

Checks Kubernetes pod status via Datadog and alerts the platform Slack channel if pods are unhealthy.

naftiko: "0.5"
info:
  label: "Kubernetes Pod Health Check"
  description: "Checks Kubernetes pod status via Datadog and alerts the platform Slack channel if pods are unhealthy."
  tags:
    - infrastructure
    - containers
    - kubernetes
    - datadog
    - slack
capability:
  exposes:
    - type: mcp
      namespace: platform-ops
      port: 8080
      tools:
        - name: check-pod-health
          description: "Given a Kubernetes namespace and deployment, check pod health via Datadog and alert Slack."
          inputParameters:
            - name: namespace
              in: body
              type: string
              description: "Kubernetes namespace."
            - name: deployment
              in: body
              type: string
              description: "Deployment name."
          steps:
            - name: query-health
              type: call
              call: datadog.query-timeseries
              with:
                query: "avg:kubernetes.pods.running{kube_namespace:{{namespace}},kube_deployment:{{deployment}}}"
            - name: alert-if-unhealthy
              type: call
              call: slack.post-message
              with:
                channel: "platform-ops"
                text: "Pod health for {{namespace}}/{{deployment}}: {{query-health.series[0].pointlist[-1][1]}} running pods"
  consumes:
    - type: http
      namespace: datadog
      baseUri: "https://api.datadoghq.com/api/v1"
      authentication:
        type: apikey
        key: "DD-API-KEY"
        value: "$secrets.datadog_api_key"
        placement: header
      resources:
        - name: query
          path: "/query"
          operations:
            - name: query-timeseries
              method: GET
    - type: http
      namespace: slack
      baseUri: "https://slack.com/api"
      authentication:
        type: bearer
        token: "$secrets.slack_bot_token"
      resources:
        - name: messages
          path: "/chat.postMessage"
          operations:
            - name: post-message
              method: POST

Fetches Elsevier LinkedIn page post engagement metrics, aggregates them in Snowflake, and sends a weekly performance digest to the social media team via Teams.

naftiko: "0.5"
info:
  label: "LinkedIn Content Performance Digest"
  description: "Fetches Elsevier LinkedIn page post engagement metrics, aggregates them in Snowflake, and sends a weekly performance digest to the social media team via Teams."
  tags:
    - marketing
    - social
    - linkedin
    - snowflake
    - msteams
    - reporting
capability:
  exposes:
    - type: mcp
      namespace: social-reporting
      port: 8080
      tools:
        - name: digest-linkedin-performance
          description: "Given a LinkedIn organization ID and a date range, fetch post engagement stats, write aggregates to Snowflake, and send a Teams digest to the social media channel. Use for weekly social performance reviews."
          inputParameters:
            - name: organization_id
              in: body
              type: string
              description: "LinkedIn organization ID (URN) for the Elsevier company page."
            - name: start_date
              in: body
              type: string
              description: "Report start date in ISO 8601 format (YYYY-MM-DD)."
            - name: end_date
              in: body
              type: string
              description: "Report end date in ISO 8601 format (YYYY-MM-DD)."
          steps:
            - name: get-post-stats
              type: call
              call: linkedin.get-share-statistics
              with:
                organization_id: "{{organization_id}}"
                start: "{{start_date}}"
                end: "{{end_date}}"
            - name: write-stats
              type: call
              call: snowflake.insert-social-row
              with:
                platform: "linkedin"
                impressions: "{{get-post-stats.totalImpressions}}"
                engagements: "{{get-post-stats.totalEngagements}}"
                start_date: "{{start_date}}"
            - name: send-digest
              type: call
              call: msteams.send-message
              with:
                channel: "social-media"
                text: "LinkedIn Performance {{start_date}} to {{end_date}}: {{get-post-stats.totalImpressions}} impressions, {{get-post-stats.totalEngagements}} engagements."
  consumes:
    - type: http
      namespace: linkedin
      baseUri: "https://api.linkedin.com/v2"
      authentication:
        type: bearer
        token: "$secrets.linkedin_token"
      resources:
        - name: share-statistics
          path: "/organizationalEntityShareStatistics"
          inputParameters:
            - name: organization_id
              in: query
            - name: start
              in: query
            - name: end
              in: query
          operations:
            - name: get-share-statistics
              method: GET
    - type: http
      namespace: snowflake
      baseUri: "https://elsevier.snowflakecomputing.com/api/v2"
      authentication:
        type: bearer
        token: "$secrets.snowflake_token"
      resources:
        - name: social-stats
          path: "/statements"
          operations:
            - name: insert-social-row
              method: POST
    - type: http
      namespace: msteams
      baseUri: "https://graph.microsoft.com/v1.0"
      authentication:
        type: bearer
        token: "$secrets.msteams_token"
      resources:
        - name: channel-messages
          path: "/teams/marketing/channels/social/messages"
          operations:
            - name: send-message
              method: POST

Fetches LinkedIn company page analytics and posts a weekly engagement summary to the marketing Slack channel.

naftiko: "0.5"
info:
  label: "LinkedIn Thought Leadership Digest"
  description: "Fetches LinkedIn company page analytics and posts a weekly engagement summary to the marketing Slack channel."
  tags:
    - marketing
    - social-media
    - linkedin
    - slack
capability:
  exposes:
    - type: mcp
      namespace: social-ops
      port: 8080
      tools:
        - name: post-linkedin-digest
          description: "Fetch LinkedIn page analytics and post a weekly digest to Slack."
          inputParameters:
            - name: org_id
              in: body
              type: string
              description: "LinkedIn organization ID."
          steps:
            - name: get-analytics
              type: call
              call: linkedin.get-page-analytics
              with:
                organization: "{{org_id}}"
            - name: post-digest
              type: call
              call: slack.post-message
              with:
                channel: "marketing-social"
                text: "LinkedIn weekly digest: {{get-analytics.impressions}} impressions, {{get-analytics.engagement_rate}}% engagement"
  consumes:
    - type: http
      namespace: linkedin
      baseUri: "https://api.linkedin.com/v2"
      authentication:
        type: bearer
        token: "$secrets.linkedin_token"
      resources:
        - name: analytics
          path: "/organizationalEntityShareStatistics"
          operations:
            - name: get-page-analytics
              method: GET
    - type: http
      namespace: slack
      baseUri: "https://slack.com/api"
      authentication:
        type: bearer
        token: "$secrets.slack_bot_token"
      resources:
        - name: messages
          path: "/chat.postMessage"
          operations:
            - name: post-message
              method: POST

Retrieves open rate and click rate for a Mailchimp journal newsletter campaign.

naftiko: "0.5"
info:
  label: "Mailchimp Newsletter Performance"
  description: "Retrieves open rate and click rate for a Mailchimp journal newsletter campaign."
  tags:
    - marketing
    - email
    - mailchimp
capability:
  exposes:
    - type: mcp
      namespace: email-ops
      port: 8080
      tools:
        - name: get-newsletter-stats
          description: "Given a Mailchimp campaign ID, return open rate and click rate."
          inputParameters:
            - name: campaign_id
              in: body
              type: string
              description: "Mailchimp campaign ID."
          call: mailchimp.get-report
          with:
            campaign_id: "{{campaign_id}}"
          outputParameters:
            - name: open_rate
              type: number
              mapping: "$.opens.open_rate"
            - name: click_rate
              type: number
              mapping: "$.clicks.click_rate"
  consumes:
    - type: http
      namespace: mailchimp
      baseUri: "https://us1.api.mailchimp.com/3.0"
      authentication:
        type: basic
        username: "anystring"
        password: "$secrets.mailchimp_api_key"
      resources:
        - name: reports
          path: "/reports/{{campaign_id}}"
          inputParameters:
            - name: campaign_id
              in: path
          operations:
            - name: get-report
              method: GET

Submits a manuscript for plagiarism screening and posts the similarity score to the editorial Slack channel.

naftiko: "0.5"
info:
  label: "Manuscript Plagiarism Check Trigger"
  description: "Submits a manuscript for plagiarism screening and posts the similarity score to the editorial Slack channel."
  tags:
    - publishing
    - editorial
    - ithenticate
    - slack
capability:
  exposes:
    - type: mcp
      namespace: editorial-ops
      port: 8080
      tools:
        - name: check-plagiarism
          description: "Given a manuscript ID and document URL, submit for plagiarism screening and notify the editorial Slack channel with the similarity score."
          inputParameters:
            - name: manuscript_id
              in: body
              type: string
              description: "Manuscript submission ID."
            - name: document_url
              in: body
              type: string
              description: "URL of the manuscript document."
          steps:
            - name: submit-check
              type: call
              call: ithenticate.submit-document
              with:
                document_url: "{{document_url}}"
                title: "Manuscript {{manuscript_id}}"
            - name: notify-editorial
              type: call
              call: slack.post-message
              with:
                channel: "editorial-ops"
                text: "Plagiarism check submitted for manuscript {{manuscript_id}}. Submission ID: {{submit-check.id}}"
  consumes:
    - type: http
      namespace: ithenticate
      baseUri: "https://api.ithenticate.com/v2"
      authentication:
        type: bearer
        token: "$secrets.ithenticate_token"
      resources:
        - name: submissions
          path: "/submissions"
          operations:
            - name: submit-document
              method: POST
    - type: http
      namespace: slack
      baseUri: "https://slack.com/api"
      authentication:
        type: bearer
        token: "$secrets.slack_bot_token"
      resources:
        - name: messages
          path: "/chat.postMessage"
          operations:
            - name: post-message
              method: POST

Retrieves Microsoft 365 license usage data via Microsoft Graph.

naftiko: "0.5"
info:
  label: "Microsoft 365 License Report"
  description: "Retrieves Microsoft 365 license usage data via Microsoft Graph."
  tags:
    - it-operations
    - license-management
    - microsoft-365
capability:
  exposes:
    - type: mcp
      namespace: it-ops
      port: 8080
      tools:
        - name: get-license-report
          description: "Retrieve Microsoft 365 license usage data."
          inputParameters:
            - name: period
              in: body
              type: string
              description: "Report period (D7, D30)."
          call: msgraph.get-license-report
          with:
            period: "{{period}}"
          outputParameters:
            - name: data
              type: array
              mapping: "$.value"
  consumes:
    - type: http
      namespace: msgraph
      baseUri: "https://graph.microsoft.com/v1.0"
      authentication:
        type: bearer
        token: "$secrets.msgraph_token"
      resources:
        - name: reports
          path: "/reports/getOffice365ActiveUserDetail(period='{{period}}')"
          inputParameters:
            - name: period
              in: path
          operations:
            - name: get-license-report
              method: GET

Posts a journal publication alert to a Microsoft Teams channel when a new issue is published.

naftiko: "0.5"
info:
  label: "Microsoft Teams Journal Alert"
  description: "Posts a journal publication alert to a Microsoft Teams channel when a new issue is published."
  tags:
    - publishing
    - communications
    - microsoft-teams
capability:
  exposes:
    - type: mcp
      namespace: publishing-ops
      port: 8080
      tools:
        - name: post-journal-alert
          description: "Given a journal name, volume, issue, and Teams channel, post a new issue publication notification."
          inputParameters:
            - name: journal_name
              in: body
              type: string
              description: "Journal name."
            - name: volume
              in: body
              type: string
              description: "Volume number."
            - name: issue
              in: body
              type: string
              description: "Issue number."
            - name: teams_channel_id
              in: body
              type: string
              description: "Microsoft Teams channel ID."
          call: msteams.post-channel-message
          with:
            channel_id: "{{teams_channel_id}}"
            text: "New issue published: {{journal_name}} Vol. {{volume}} Issue {{issue}}"
          outputParameters:
            - name: message_id
              type: string
              mapping: "$.id"
  consumes:
    - type: http
      namespace: msteams
      baseUri: "https://graph.microsoft.com/v1.0"
      authentication:
        type: bearer
        token: "$secrets.msteams_token"
      resources:
        - name: channel-messages
          path: "/teams/{{team_id}}/channels/{{channel_id}}/messages"
          inputParameters:
            - name: channel_id
              in: path
          operations:
            - name: post-channel-message
              method: POST

When a new hire record is created in Workday, provisions a Microsoft 365 account, opens a ServiceNow IT onboarding ticket, and sends a welcome message via Microsoft Teams.

naftiko: "0.5"
info:
  label: "New Employee Onboarding"
  description: "When a new hire record is created in Workday, provisions a Microsoft 365 account, opens a ServiceNow IT onboarding ticket, and sends a welcome message via Microsoft Teams."
  tags:
    - hr
    - onboarding
    - workday
    - microsoft-365
    - servicenow
    - msteams
capability:
  exposes:
    - type: mcp
      namespace: hr-onboarding
      port: 8080
      tools:
        - name: trigger-employee-onboarding
          description: "Given a Workday employee ID and start date, provision a Microsoft 365 account, open a ServiceNow onboarding incident, and send a Teams welcome message. Use when HR confirms a new hire record."
          inputParameters:
            - name: workday_employee_id
              in: body
              type: string
              description: "Workday worker ID for the new hire."
            - name: start_date
              in: body
              type: string
              description: "Employee start date in ISO 8601 format (YYYY-MM-DD)."
          steps:
            - name: get-worker
              type: call
              call: workday.get-worker
              with:
                worker_id: "{{workday_employee_id}}"
            - name: provision-m365
              type: call
              call: msgraph.create-user
              with:
                displayName: "{{get-worker.full_name}}"
                userPrincipalName: "{{get-worker.email}}"
                department: "{{get-worker.department}}"
            - name: open-ticket
              type: call
              call: servicenow.create-incident
              with:
                short_description: "IT Onboarding: {{get-worker.full_name}} starting {{start_date}}"
                category: "onboarding"
                assignment_group: "IT_Onboarding"
            - name: send-welcome
              type: call
              call: msteams.post-welcome
              with:
                recipient_upn: "{{get-worker.email}}"
                message: "Welcome to Elsevier, {{get-worker.first_name}}! Your IT ticket: {{open-ticket.number}}"
  consumes:
    - type: http
      namespace: workday
      baseUri: "https://wd2-impl-services1.workday.com/ccx/api/v1"
      authentication:
        type: bearer
        token: "$secrets.workday_token"
      resources:
        - name: workers
          path: "/workers/{{worker_id}}"
          inputParameters:
            - name: worker_id
              in: path
          operations:
            - name: get-worker
              method: GET
    - type: http
      namespace: msgraph
      baseUri: "https://graph.microsoft.com/v1.0"
      authentication:
        type: bearer
        token: "$secrets.msgraph_token"
      resources:
        - name: users
          path: "/users"
          operations:
            - name: create-user
              method: POST
    - type: http
      namespace: servicenow
      baseUri: "https://elsevier.service-now.com/api/now"
      authentication:
        type: basic
        username: "$secrets.servicenow_user"
        password: "$secrets.servicenow_password"
      resources:
        - name: incidents
          path: "/table/incident"
          operations:
            - name: create-incident
              method: POST
    - type: http
      namespace: msteams
      baseUri: "https://graph.microsoft.com/v1.0"
      authentication:
        type: bearer
        token: "$secrets.msteams_token"
      resources:
        - name: messages
          path: "/users/{{recipient_upn}}/sendMail"
          inputParameters:
            - name: recipient_upn
              in: path
          operations:
            - name: post-welcome
              method: POST

Assigns an Okta user to an application and logs the action to ServiceNow.

naftiko: "0.5"
info:
  label: "Okta App Assignment"
  description: "Assigns an Okta user to an application and logs the action to ServiceNow."
  tags:
    - identity
    - provisioning
    - okta
    - servicenow
capability:
  exposes:
    - type: mcp
      namespace: iam-ops
      port: 8080
      tools:
        - name: assign-app
          description: "Given an Okta user ID and app ID, assign the user and log to ServiceNow."
          inputParameters:
            - name: user_id
              in: body
              type: string
              description: "Okta user ID."
            - name: app_id
              in: body
              type: string
              description: "Okta app ID."
          steps:
            - name: assign
              type: call
              call: okta.assign-app
              with:
                app_id: "{{app_id}}"
                user_id: "{{user_id}}"
            - name: log-audit
              type: call
              call: servicenow.create-record
              with:
                table: "u_access_audit"
                user_id: "{{user_id}}"
                app: "{{app_id}}"
                action: "assigned"
  consumes:
    - type: http
      namespace: okta
      baseUri: "https://elsevier.okta.com/api/v1"
      authentication:
        type: bearer
        token: "$secrets.okta_api_token"
      resources:
        - name: app-users
          path: "/apps/{{app_id}}/users"
          inputParameters:
            - name: app_id
              in: path
          operations:
            - name: assign-app
              method: POST
    - type: http
      namespace: servicenow
      baseUri: "https://elsevier.service-now.com/api/now"
      authentication:
        type: basic
        username: "$secrets.servicenow_user"
        password: "$secrets.servicenow_password"
      resources:
        - name: records
          path: "/table/{{table}}"
          inputParameters:
            - name: table
              in: path
          operations:
            - name: create-record
              method: POST

Retrieves all Okta group memberships for a user for quarterly access certification.

naftiko: "0.5"
info:
  label: "Okta User Access Review"
  description: "Retrieves all Okta group memberships for a user for quarterly access certification."
  tags:
    - identity
    - access-management
    - okta
capability:
  exposes:
    - type: mcp
      namespace: iam
      port: 8080
      tools:
        - name: get-user-groups
          description: "Given an Okta user ID, return all group memberships."
          inputParameters:
            - name: user_id
              in: body
              type: string
              description: "Okta user ID."
          call: okta.list-user-groups
          with:
            user_id: "{{user_id}}"
          outputParameters:
            - name: groups
              type: array
              mapping: "$.[*].profile.name"
  consumes:
    - type: http
      namespace: okta
      baseUri: "https://elsevier.okta.com/api/v1"
      authentication:
        type: bearer
        token: "$secrets.okta_api_token"
      resources:
        - name: user-groups
          path: "/users/{{user_id}}/groups"
          inputParameters:
            - name: user_id
              in: path
          operations:
            - name: list-user-groups
              method: GET

When an author accepts open-access publishing, creates an invoice in SAP, links it to the Salesforce opportunity, and notifies the author via email through Microsoft 365.

naftiko: "0.5"
info:
  label: "Open Access Invoice Processing"
  description: "When an author accepts open-access publishing, creates an invoice in SAP, links it to the Salesforce opportunity, and notifies the author via email through Microsoft 365."
  tags:
    - finance
    - publishing
    - open-access
    - sap
    - salesforce
    - microsoft-365
capability:
  exposes:
    - type: mcp
      namespace: oa-billing
      port: 8080
      tools:
        - name: create-oa-invoice
          description: "Given an article DOI, author institution ID, and APC amount, create a SAP billing document, link it to the Salesforce opportunity record, and send the invoice to the author via Microsoft 365 email."
          inputParameters:
            - name: article_doi
              in: body
              type: string
              description: "Article DOI for the accepted open-access manuscript."
            - name: institution_id
              in: body
              type: string
              description: "Salesforce account ID for the author's institution."
            - name: apc_amount
              in: body
              type: number
              description: "Article Processing Charge in USD."
          steps:
            - name: get-opportunity
              type: call
              call: salesforce.get-opportunity-by-doi
              with:
                doi__c: "{{article_doi}}"
            - name: create-invoice
              type: call
              call: sap.create-billing-document
              with:
                reference: "{{article_doi}}"
                payer: "{{institution_id}}"
                net_amount: "{{apc_amount}}"
            - name: update-opportunity
              type: call
              call: salesforce-write.patch-opportunity
              with:
                opportunity_id: "{{get-opportunity.id}}"
                SAP_Invoice_Number__c: "{{create-invoice.billing_doc_number}}"
                StageName: "Invoice Sent"
  consumes:
    - type: http
      namespace: salesforce
      baseUri: "https://elsevier.my.salesforce.com/services/data/v58.0"
      authentication:
        type: bearer
        token: "$secrets.salesforce_token"
      resources:
        - name: opportunities
          path: "/query"
          inputParameters:
            - name: doi__c
              in: query
          operations:
            - name: get-opportunity-by-doi
              method: GET
    - type: http
      namespace: sap
      baseUri: "https://elsevier-s4.sap.com/sap/opu/odata/sap/SD_BILLING_SRV"
      authentication:
        type: basic
        username: "$secrets.sap_user"
        password: "$secrets.sap_password"
      resources:
        - name: billing-documents
          path: "/A_BillingDocument"
          operations:
            - name: create-billing-document
              method: POST
    - type: http
      namespace: salesforce-write
      baseUri: "https://elsevier.my.salesforce.com/services/data/v58.0"
      authentication:
        type: bearer
        token: "$secrets.salesforce_token"
      resources:
        - name: opportunity-record
          path: "/sobjects/Opportunity/{{opportunity_id}}"
          inputParameters:
            - name: opportunity_id
              in: path
          operations:
            - name: patch-opportunity
              method: PATCH

Uses OpenAI to generate a plain-language summary of an article abstract and stores it in Snowflake for discoverability.

naftiko: "0.5"
info:
  label: "OpenAI Article Abstract Generation"
  description: "Uses OpenAI to generate a plain-language summary of an article abstract and stores it in Snowflake for discoverability."
  tags:
    - ai
    - publishing
    - openai
    - snowflake
capability:
  exposes:
    - type: mcp
      namespace: content-ai
      port: 8080
      tools:
        - name: generate-plain-summary
          description: "Given an article DOI and abstract text, generate a plain-language summary via OpenAI and store in Snowflake."
          inputParameters:
            - name: doi
              in: body
              type: string
              description: "Article DOI."
            - name: abstract
              in: body
              type: string
              description: "Original article abstract."
          steps:
            - name: generate-summary
              type: call
              call: openai.create-completion
              with:
                model: "gpt-4"
                prompt: "Write a plain-language summary of this scientific abstract: {{abstract}}"
            - name: store-summary
              type: call
              call: snowflake.insert-row
              with:
                table: "ARTICLE_SUMMARIES"
                values:
                  doi: "{{doi}}"
                  plain_summary: "{{generate-summary.text}}"
  consumes:
    - type: http
      namespace: openai
      baseUri: "https://api.openai.com/v1"
      authentication:
        type: bearer
        token: "$secrets.openai_api_key"
      resources:
        - name: completions
          path: "/chat/completions"
          operations:
            - name: create-completion
              method: POST
    - type: http
      namespace: snowflake
      baseUri: "https://elsevier.snowflakecomputing.com/api/v2"
      authentication:
        type: bearer
        token: "$secrets.snowflake_token"
      resources:
        - name: statements
          path: "/statements"
          operations:
            - name: insert-row
              method: POST

Uses OpenAI to analyze a manuscript abstract and suggest potential peer reviewers from the Scopus reviewer pool, then posts suggestions to Microsoft Teams.

naftiko: "0.5"
info:
  label: "OpenAI Peer Review Matching"
  description: "Uses OpenAI to analyze a manuscript abstract and suggest potential peer reviewers from the Scopus reviewer pool, then posts suggestions to Microsoft Teams."
  tags:
    - ai
    - publishing
    - openai
    - microsoft-teams
capability:
  exposes:
    - type: mcp
      namespace: editorial-ai
      port: 8080
      tools:
        - name: suggest-reviewers
          description: "Given an abstract, use OpenAI to extract key topics and suggest reviewer matches. Post suggestions to Teams."
          inputParameters:
            - name: manuscript_id
              in: body
              type: string
              description: "Manuscript ID."
            - name: abstract
              in: body
              type: string
              description: "Manuscript abstract text."
          steps:
            - name: analyze-topics
              type: call
              call: openai.create-completion
              with:
                model: "gpt-4"
                prompt: "Extract 5 key research topics from this abstract for peer reviewer matching: {{abstract}}"
            - name: post-suggestions
              type: call
              call: msteams.post-channel-message
              with:
                channel_id: "editorial-ops"
                text: "Reviewer suggestions for {{manuscript_id}}: {{analyze-topics.text}}"
  consumes:
    - type: http
      namespace: openai
      baseUri: "https://api.openai.com/v1"
      authentication:
        type: bearer
        token: "$secrets.openai_api_key"
      resources:
        - name: completions
          path: "/chat/completions"
          operations:
            - name: create-completion
              method: POST
    - type: http
      namespace: msteams
      baseUri: "https://graph.microsoft.com/v1.0"
      authentication:
        type: bearer
        token: "$secrets.msteams_token"
      resources:
        - name: channel-messages
          path: "/teams/{{team_id}}/channels/{{channel_id}}/messages"
          inputParameters:
            - name: channel_id
              in: path
          operations:
            - name: post-channel-message
              method: POST

Retrieves a researcher profile from ORCID by ORCID ID, returning name, affiliations, and publication count.

naftiko: "0.5"
info:
  label: "ORCID Researcher Profile Lookup"
  description: "Retrieves a researcher profile from ORCID by ORCID ID, returning name, affiliations, and publication count."
  tags:
    - research
    - publishing
    - orcid
capability:
  exposes:
    - type: mcp
      namespace: research-ops
      port: 8080
      tools:
        - name: get-researcher-profile
          description: "Given an ORCID ID, return the researcher's name, current affiliation, and works count."
          inputParameters:
            - name: orcid_id
              in: body
              type: string
              description: "ORCID ID (e.g., 0000-0002-1234-5678)."
          call: orcid.get-record
          with:
            orcid_id: "{{orcid_id}}"
          outputParameters:
            - name: name
              type: string
              mapping: "$.person.name.given-names.value"
            - name: affiliation
              type: string
              mapping: "$.activities-summary.employments.affiliation-group[0].summaries[0].employment-summary.organization.name"
  consumes:
    - type: http
      namespace: orcid
      baseUri: "https://pub.orcid.org/v3.0"
      authentication:
        type: bearer
        token: "$secrets.orcid_token"
      resources:
        - name: records
          path: "/{{orcid_id}}/record"
          inputParameters:
            - name: orcid_id
              in: path
          operations:
            - name: get-record
              method: GET

Creates a Jira ticket from a PagerDuty incident and notifies the engineering Slack channel.

naftiko: "0.5"
info:
  label: "PagerDuty Incident to Jira"
  description: "Creates a Jira ticket from a PagerDuty incident and notifies the engineering Slack channel."
  tags:
    - incident-management
    - engineering
    - pagerduty
    - jira
    - slack
capability:
  exposes:
    - type: mcp
      namespace: incident-ops
      port: 8080
      tools:
        - name: create-jira-from-pd
          description: "Given a PagerDuty incident ID, fetch details and create a Jira ticket."
          inputParameters:
            - name: incident_id
              in: body
              type: string
              description: "PagerDuty incident ID."
          steps:
            - name: get-pd
              type: call
              call: pagerduty.get-incident
              with:
                incident_id: "{{incident_id}}"
            - name: create-jira
              type: call
              call: jira.create-issue
              with:
                project_key: "OPS"
                issuetype: "Bug"
                summary: "PD: {{get-pd.incident.title}}"
            - name: notify-eng
              type: call
              call: slack.post-message
              with:
                channel: "engineering"
                text: "PD incident {{incident_id}} -> Jira {{create-jira.key}}"
  consumes:
    - type: http
      namespace: pagerduty
      baseUri: "https://api.pagerduty.com"
      authentication:
        type: bearer
        token: "$secrets.pagerduty_token"
      resources:
        - name: incidents
          path: "/incidents/{{incident_id}}"
          inputParameters:
            - name: incident_id
              in: path
          operations:
            - name: get-incident
              method: GET
    - type: http
      namespace: jira
      baseUri: "https://elsevier.atlassian.net/rest/api/3"
      authentication:
        type: basic
        username: "$secrets.jira_user"
        password: "$secrets.jira_token"
      resources:
        - name: issues
          path: "/issue"
          operations:
            - name: create-issue
              method: POST
    - type: http
      namespace: slack
      baseUri: "https://slack.com/api"
      authentication:
        type: bearer
        token: "$secrets.slack_bot_token"
      resources:
        - name: messages
          path: "/chat.postMessage"
          operations:
            - name: post-message
              method: POST

Retrieves the current on-call engineer for a PagerDuty schedule.

naftiko: "0.5"
info:
  label: "PagerDuty On-Call Lookup"
  description: "Retrieves the current on-call engineer for a PagerDuty schedule."
  tags:
    - engineering
    - incident-management
    - pagerduty
capability:
  exposes:
    - type: mcp
      namespace: incident-ops
      port: 8080
      tools:
        - name: get-on-call
          description: "Given a PagerDuty schedule ID, return the current on-call engineer's name and email."
          inputParameters:
            - name: schedule_id
              in: body
              type: string
              description: "PagerDuty schedule ID."
          call: pagerduty.get-on-call
          with:
            schedule_id: "{{schedule_id}}"
          outputParameters:
            - name: name
              type: string
              mapping: "$.schedule.final_schedule.rendered_schedule_entries[0].user.summary"
            - name: email
              type: string
              mapping: "$.schedule.final_schedule.rendered_schedule_entries[0].user.email"
  consumes:
    - type: http
      namespace: pagerduty
      baseUri: "https://api.pagerduty.com"
      authentication:
        type: bearer
        token: "$secrets.pagerduty_token"
      resources:
        - name: schedules
          path: "/schedules/{{schedule_id}}"
          inputParameters:
            - name: schedule_id
              in: path
          operations:
            - name: get-on-call
              method: GET

Identifies qualified reviewers for a manuscript by querying Scopus author expertise, checks conflicts of interest in Salesforce, and creates reviewer tasks in ServiceNow.

naftiko: "0.5"
info:
  label: "Peer Review Assignment"
  description: "Identifies qualified reviewers for a manuscript by querying Scopus author expertise, checks conflicts of interest in Salesforce, and creates reviewer tasks in ServiceNow."
  tags:
    - publishing
    - peer-review
    - scopus
    - salesforce
    - servicenow
capability:
  exposes:
    - type: mcp
      namespace: peer-review
      port: 8080
      tools:
        - name: assign-reviewers
          description: "Given a manuscript ID, subject keywords, and handling editor ID, query Scopus for top-cited authors in the field, screen them for conflicts in Salesforce, and open reviewer tasks in ServiceNow."
          inputParameters:
            - name: manuscript_id
              in: body
              type: string
              description: "Manuscript identifier from the editorial system."
            - name: subject_keywords
              in: body
              type: string
              description: "Comma-separated subject keywords for expert matching (e.g., 'proteomics,mass spectrometry')."
            - name: editor_id
              in: body
              type: string
              description: "Workday employee ID of the handling editor."
          steps:
            - name: find-experts
              type: call
              call: scopus.search-authors-by-subject
              with:
                keywords: "{{subject_keywords}}"
                limit: "5"
            - name: create-review-task
              type: call
              call: servicenow.create-task
              with:
                short_description: "Peer review assignment: {{manuscript_id}}"
                assignment_group: "Editorial_Operations"
                description: "Manuscript {{manuscript_id}} requires reviewer assignment. Top candidates fetched from Scopus: {{find-experts.author_names}}"
  consumes:
    - type: http
      namespace: scopus
      baseUri: "https://api.elsevier.com/content/search/author"
      authentication:
        type: apikey
        key: "X-ELS-APIKey"
        value: "$secrets.scopus_api_key"
        placement: header
      resources:
        - name: author-search
          path: "/"
          inputParameters:
            - name: keywords
              in: query
            - name: limit
              in: query
          operations:
            - name: search-authors-by-subject
              method: GET
    - type: http
      namespace: servicenow
      baseUri: "https://elsevier.service-now.com/api/now"
      authentication:
        type: basic
        username: "$secrets.servicenow_user"
        password: "$secrets.servicenow_password"
      resources:
        - name: tasks
          path: "/table/task"
          operations:
            - name: create-task
              method: POST

Retrieves the latest run results for a Postman API monitor.

naftiko: "0.5"
info:
  label: "Postman API Health Monitor"
  description: "Retrieves the latest run results for a Postman API monitor."
  tags:
    - engineering
    - api-management
    - postman
capability:
  exposes:
    - type: mcp
      namespace: api-ops
      port: 8080
      tools:
        - name: get-monitor-status
          description: "Given a Postman monitor ID, return pass/fail counts."
          inputParameters:
            - name: monitor_id
              in: body
              type: string
              description: "Postman monitor ID."
          call: postman.get-monitor
          with:
            monitor_id: "{{monitor_id}}"
          outputParameters:
            - name: status
              type: string
              mapping: "$.monitor.lastRun.status"
            - name: passed
              type: integer
              mapping: "$.monitor.lastRun.stats.assertions.passed"
  consumes:
    - type: http
      namespace: postman
      baseUri: "https://api.getpostman.com"
      authentication:
        type: apikey
        key: "X-Api-Key"
        value: "$secrets.postman_api_key"
        placement: header
      resources:
        - name: monitors
          path: "/monitors/{{monitor_id}}"
          inputParameters:
            - name: monitor_id
              in: path
          operations:
            - name: get-monitor
              method: GET

Triggers a scheduled Power BI dataset refresh for the editorial operations dashboard, verifying completion status and alerting via Teams on failure.

naftiko: "0.5"
info:
  label: "Power BI Editorial Dashboard Refresh"
  description: "Triggers a scheduled Power BI dataset refresh for the editorial operations dashboard, verifying completion status and alerting via Teams on failure."
  tags:
    - analytics
    - reporting
    - power-bi
    - msteams
    - monitoring
capability:
  exposes:
    - type: mcp
      namespace: editorial-reporting
      port: 8080
      tools:
        - name: refresh-editorial-dashboard
          description: "Given a Power BI workspace ID and dataset ID, trigger a dataset refresh, poll for completion, and post a Teams notification with the refresh outcome. Use for daily editorial KPI dashboard updates."
          inputParameters:
            - name: workspace_id
              in: body
              type: string
              description: "Power BI workspace (group) ID containing the editorial dataset."
            - name: dataset_id
              in: body
              type: string
              description: "Power BI dataset ID to refresh."
          steps:
            - name: trigger-refresh
              type: call
              call: powerbi.trigger-dataset-refresh
              with:
                workspace_id: "{{workspace_id}}"
                dataset_id: "{{dataset_id}}"
            - name: notify-completion
              type: call
              call: msteams.send-message
              with:
                channel: "editorial-ops"
                text: "Power BI editorial dashboard refresh triggered for dataset {{dataset_id}} in workspace {{workspace_id}}."
  consumes:
    - type: http
      namespace: powerbi
      baseUri: "https://api.powerbi.com/v1.0/myorg"
      authentication:
        type: bearer
        token: "$secrets.powerbi_token"
      resources:
        - name: dataset-refreshes
          path: "/groups/{{workspace_id}}/datasets/{{dataset_id}}/refreshes"
          inputParameters:
            - name: workspace_id
              in: path
            - name: dataset_id
              in: path
          operations:
            - name: trigger-dataset-refresh
              method: POST
    - type: http
      namespace: msteams
      baseUri: "https://graph.microsoft.com/v1.0"
      authentication:
        type: bearer
        token: "$secrets.msteams_token"
      resources:
        - name: channel-messages
          path: "/teams/editorial/channels/general/messages"
          operations:
            - name: send-message
              method: POST

Triggers a Power BI dataset refresh for platform usage analytics.

naftiko: "0.5"
info:
  label: "Power BI Usage Dashboard Refresh"
  description: "Triggers a Power BI dataset refresh for platform usage analytics."
  tags:
    - analytics
    - reporting
    - power-bi
capability:
  exposes:
    - type: mcp
      namespace: bi-ops
      port: 8080
      tools:
        - name: refresh-usage-dataset
          description: "Given a Power BI dataset ID and workspace, trigger a refresh."
          inputParameters:
            - name: dataset_id
              in: body
              type: string
              description: "Power BI dataset ID."
            - name: group_id
              in: body
              type: string
              description: "Power BI workspace group ID."
          call: powerbi.refresh-dataset
          with:
            group_id: "{{group_id}}"
            dataset_id: "{{dataset_id}}"
          outputParameters:
            - name: request_id
              type: string
              mapping: "$.requestId"
  consumes:
    - type: http
      namespace: powerbi
      baseUri: "https://api.powerbi.com/v1.0/myorg"
      authentication:
        type: bearer
        token: "$secrets.powerbi_token"
      resources:
        - name: datasets
          path: "/groups/{{group_id}}/datasets/{{dataset_id}}/refreshes"
          inputParameters:
            - name: group_id
              in: path
            - name: dataset_id
              in: path
          operations:
            - name: refresh-dataset
              method: POST

Enriches an author record in Salesforce with current affiliation and citation metrics fetched from Scopus, then updates the CRM contact.

naftiko: "0.5"
info:
  label: "Researcher Profile Enrichment"
  description: "Enriches an author record in Salesforce with current affiliation and citation metrics fetched from Scopus, then updates the CRM contact."
  tags:
    - publishing
    - crm
    - data-enrichment
    - salesforce
    - scopus
    - reporting
capability:
  exposes:
    - type: mcp
      namespace: researcher-data
      port: 8080
      tools:
        - name: enrich-author-profile
          description: "Given an author Scopus ID, fetch current institution affiliation and h-index from Scopus, then patch the Salesforce contact record with enriched data. Use during author onboarding or annual data refresh."
          inputParameters:
            - name: scopus_author_id
              in: body
              type: string
              description: "Scopus numeric author identifier (e.g., 7402691548)."
            - name: salesforce_contact_id
              in: body
              type: string
              description: "Salesforce Contact record ID to update."
          steps:
            - name: fetch-scopus-profile
              type: call
              call: scopus.get-author
              with:
                author_id: "{{scopus_author_id}}"
            - name: update-contact
              type: call
              call: salesforce-update.patch-contact
              with:
                contact_id: "{{salesforce_contact_id}}"
                Current_Affiliation__c: "{{fetch-scopus-profile.affiliation}}"
                H_Index__c: "{{fetch-scopus-profile.h_index}}"
                Citation_Count__c: "{{fetch-scopus-profile.citation_count}}"
  consumes:
    - type: http
      namespace: scopus
      baseUri: "https://api.elsevier.com/content/author"
      authentication:
        type: apikey
        key: "X-ELS-APIKey"
        value: "$secrets.scopus_api_key"
        placement: header
      resources:
        - name: author-profile
          path: "/author_id/{{author_id}}"
          inputParameters:
            - name: author_id
              in: path
          operations:
            - name: get-author
              method: GET
    - type: http
      namespace: salesforce-update
      baseUri: "https://elsevier.my.salesforce.com/services/data/v58.0"
      authentication:
        type: bearer
        token: "$secrets.salesforce_token"
      resources:
        - name: contacts
          path: "/sobjects/Contact/{{contact_id}}"
          inputParameters:
            - name: contact_id
              in: path
          operations:
            - name: patch-contact
              method: PATCH

Retrieves a Salesforce contact by email, returning name, title, account, and phone.

naftiko: "0.5"
info:
  label: "Salesforce Contact Lookup"
  description: "Retrieves a Salesforce contact by email, returning name, title, account, and phone."
  tags:
    - sales
    - crm
    - salesforce
capability:
  exposes:
    - type: mcp
      namespace: crm-ops
      port: 8080
      tools:
        - name: lookup-contact
          description: "Given an email, look up the Salesforce contact record."
          inputParameters:
            - name: email
              in: body
              type: string
              description: "Contact email."
          call: salesforce.query-contact
          with:
            q: "SELECT Name, Title, Account.Name FROM Contact WHERE Email = '{{email}}'"
          outputParameters:
            - name: name
              type: string
              mapping: "$.records[0].Name"
            - name: account
              type: string
              mapping: "$.records[0].Account.Name"
  consumes:
    - type: http
      namespace: salesforce
      baseUri: "https://elsevier.my.salesforce.com/services/data/v58.0"
      authentication:
        type: bearer
        token: "$secrets.salesforce_token"
      resources:
        - name: query
          path: "/query"
          operations:
            - name: query-contact
              method: GET

Queries Salesforce for institutional subscriptions expiring within 60 days and posts the list to the sales Slack channel.

naftiko: "0.5"
info:
  label: "Salesforce Institutional Renewal Alert"
  description: "Queries Salesforce for institutional subscriptions expiring within 60 days and posts the list to the sales Slack channel."
  tags:
    - sales
    - subscriptions
    - salesforce
    - slack
capability:
  exposes:
    - type: mcp
      namespace: sales-ops
      port: 8080
      tools:
        - name: alert-upcoming-renewals
          description: "Fetch institutional subscriptions expiring within a threshold and notify the sales team via Slack."
          inputParameters:
            - name: days_threshold
              in: body
              type: integer
              description: "Days until expiry threshold."
          steps:
            - name: query-renewals
              type: call
              call: salesforce.query
              with:
                q: "SELECT Account.Name, EndDate, Amount FROM Contract WHERE EndDate = NEXT_N_DAYS:{{days_threshold}} AND Status = 'Active'"
            - name: post-alert
              type: call
              call: slack.post-message
              with:
                channel: "sales-renewals"
                text: "Upcoming renewals (next {{days_threshold}} days): {{query-renewals.totalSize}} contracts expiring"
  consumes:
    - type: http
      namespace: salesforce
      baseUri: "https://elsevier.my.salesforce.com/services/data/v58.0"
      authentication:
        type: bearer
        token: "$secrets.salesforce_token"
      resources:
        - name: query
          path: "/query"
          operations:
            - name: query
              method: GET
    - type: http
      namespace: slack
      baseUri: "https://slack.com/api"
      authentication:
        type: bearer
        token: "$secrets.slack_bot_token"
      resources:
        - name: messages
          path: "/chat.postMessage"
          operations:
            - name: post-message
              method: POST

Pulls open opportunity pipeline from Salesforce, writes the forecast data to Snowflake, and triggers a Power BI quarterly revenue forecast refresh.

naftiko: "0.5"
info:
  label: "Salesforce Opportunity Forecast Sync"
  description: "Pulls open opportunity pipeline from Salesforce, writes the forecast data to Snowflake, and triggers a Power BI quarterly revenue forecast refresh."
  tags:
    - sales
    - finance
    - crm
    - salesforce
    - snowflake
    - power-bi
    - forecasting
capability:
  exposes:
    - type: mcp
      namespace: sales-forecasting
      port: 8080
      tools:
        - name: sync-opportunity-forecast
          description: "Given a Salesforce fiscal quarter label, export open and committed opportunities, write them to Snowflake, and trigger a Power BI forecast refresh. Use before quarterly business review preparation."
          inputParameters:
            - name: fiscal_quarter
              in: body
              type: string
              description: "Salesforce fiscal quarter label (e.g., Q2-2026)."
          steps:
            - name: export-opportunities
              type: call
              call: salesforce.query-open-opportunities
              with:
                fiscal_quarter: "{{fiscal_quarter}}"
            - name: write-forecast
              type: call
              call: snowflake.insert-forecast-rows
              with:
                fiscal_quarter: "{{fiscal_quarter}}"
                records: "{{export-opportunities.records}}"
            - name: refresh-powerbi
              type: call
              call: powerbi.trigger-refresh
              with:
                dataset_id: "$secrets.powerbi_forecast_dataset_id"
  consumes:
    - type: http
      namespace: salesforce
      baseUri: "https://elsevier.my.salesforce.com/services/data/v58.0"
      authentication:
        type: bearer
        token: "$secrets.salesforce_token"
      resources:
        - name: opportunities
          path: "/query"
          inputParameters:
            - name: fiscal_quarter
              in: query
          operations:
            - name: query-open-opportunities
              method: GET
    - type: http
      namespace: snowflake
      baseUri: "https://elsevier.snowflakecomputing.com/api/v2"
      authentication:
        type: bearer
        token: "$secrets.snowflake_token"
      resources:
        - name: forecast-table
          path: "/statements"
          operations:
            - name: insert-forecast-rows
              method: POST
    - type: http
      namespace: powerbi
      baseUri: "https://api.powerbi.com/v1.0/myorg"
      authentication:
        type: bearer
        token: "$secrets.powerbi_token"
      resources:
        - name: dataset-refreshes
          path: "/datasets/{{dataset_id}}/refreshes"
          inputParameters:
            - name: dataset_id
              in: path
          operations:
            - name: trigger-refresh
              method: POST

Retrieves a SAP Ariba contract by ID, returning status, vendor, value, and expiry date.

naftiko: "0.5"
info:
  label: "SAP Ariba Contract Status Lookup"
  description: "Retrieves a SAP Ariba contract by ID, returning status, vendor, value, and expiry date."
  tags:
    - procurement
    - contracts
    - sap-ariba
capability:
  exposes:
    - type: mcp
      namespace: procurement-ops
      port: 8080
      tools:
        - name: get-contract-status
          description: "Given a SAP Ariba contract ID, return status, vendor name, total value, and expiry date."
          inputParameters:
            - name: contract_id
              in: body
              type: string
              description: "SAP Ariba contract ID."
          call: ariba.get-contract
          with:
            contract_id: "{{contract_id}}"
          outputParameters:
            - name: status
              type: string
              mapping: "$.status"
            - name: vendor
              type: string
              mapping: "$.supplier.name"
            - name: expiry_date
              type: string
              mapping: "$.expirationDate"
  consumes:
    - type: http
      namespace: ariba
      baseUri: "https://openapi.ariba.com/api/contract-compliance/v1"
      authentication:
        type: bearer
        token: "$secrets.ariba_token"
      resources:
        - name: contracts
          path: "/contracts/{{contract_id}}"
          inputParameters:
            - name: contract_id
              in: path
          operations:
            - name: get-contract
              method: GET

When a purchase requisition exceeds the approval threshold in SAP Ariba, looks up the approver in Workday, creates an approval task in ServiceNow, and notifies the approver via Teams.

naftiko: "0.5"
info:
  label: "SAP Ariba Purchase Order Approval"
  description: "When a purchase requisition exceeds the approval threshold in SAP Ariba, looks up the approver in Workday, creates an approval task in ServiceNow, and notifies the approver via Teams."
  tags:
    - procurement
    - finance
    - sap-ariba
    - workday
    - servicenow
    - approval
capability:
  exposes:
    - type: mcp
      namespace: procurement-approval
      port: 8080
      tools:
        - name: trigger-po-approval
          description: "Given an SAP Ariba requisition ID and amount, look up the cost center approver in Workday, open a ServiceNow approval task, and send a Teams notification to the approver. Use for POs above the $50k auto-approve threshold."
          inputParameters:
            - name: requisition_id
              in: body
              type: string
              description: "SAP Ariba purchase requisition ID."
            - name: amount
              in: body
              type: number
              description: "Requisition total amount in USD."
            - name: cost_center
              in: body
              type: string
              description: "SAP cost center code for the purchase."
          steps:
            - name: get-approver
              type: call
              call: workday.get-cost-center-manager
              with:
                cost_center: "{{cost_center}}"
            - name: create-approval-task
              type: call
              call: servicenow.create-task
              with:
                short_description: "PO Approval Required: {{requisition_id}} — ${{amount}}"
                assignment: "{{get-approver.email}}"
                description: "Ariba requisition {{requisition_id}} requires approval. Amount: ${{amount}}. Cost center: {{cost_center}}"
            - name: notify-approver
              type: call
              call: msteams.send-message
              with:
                recipient_upn: "{{get-approver.email}}"
                text: "Action required: PO approval for requisition {{requisition_id}} (${{amount}}). Task: {{create-approval-task.number}}"
  consumes:
    - type: http
      namespace: workday
      baseUri: "https://wd2-impl-services1.workday.com/ccx/api/v1"
      authentication:
        type: bearer
        token: "$secrets.workday_token"
      resources:
        - name: cost-center-managers
          path: "/costCenters/{{cost_center}}/manager"
          inputParameters:
            - name: cost_center
              in: path
          operations:
            - name: get-cost-center-manager
              method: GET
    - type: http
      namespace: servicenow
      baseUri: "https://elsevier.service-now.com/api/now"
      authentication:
        type: basic
        username: "$secrets.servicenow_user"
        password: "$secrets.servicenow_password"
      resources:
        - name: tasks
          path: "/table/task"
          operations:
            - name: create-task
              method: POST
    - type: http
      namespace: msteams
      baseUri: "https://graph.microsoft.com/v1.0"
      authentication:
        type: bearer
        token: "$secrets.msteams_token"
      resources:
        - name: messages
          path: "/users/{{recipient_upn}}/sendMail"
          inputParameters:
            - name: recipient_upn
              in: path
          operations:
            - name: send-message
              method: POST

Queries SAP Concur for unsubmitted expense reports and sends reminders via Microsoft Graph.

naftiko: "0.5"
info:
  label: "SAP Concur Expense Reminder"
  description: "Queries SAP Concur for unsubmitted expense reports and sends reminders via Microsoft Graph."
  tags:
    - finance
    - expense-management
    - sap-concur
    - microsoft-teams
capability:
  exposes:
    - type: mcp
      namespace: finance-ops
      port: 8080
      tools:
        - name: send-expense-reminders
          description: "Fetch unsubmitted Concur reports and send email reminders."
          inputParameters:
            - name: days_threshold
              in: body
              type: integer
              description: "Days since creation threshold."
          steps:
            - name: get-unsubmitted
              type: call
              call: concur.list-reports
              with:
                status: "UNSUBMITTED"
                older_than_days: "{{days_threshold}}"
            - name: send-reminders
              type: call
              call: msgraph.send-mail
              with:
                recipients: "{{get-unsubmitted.owner_emails}}"
                subject: "Please submit your expense report"
                body: "You have an unsubmitted expense report older than {{days_threshold}} days."
  consumes:
    - type: http
      namespace: concur
      baseUri: "https://us.api.concursolutions.com/api/v3.0"
      authentication:
        type: bearer
        token: "$secrets.concur_token"
      resources:
        - name: reports
          path: "/expense/reports"
          operations:
            - name: list-reports
              method: GET
    - type: http
      namespace: msgraph
      baseUri: "https://graph.microsoft.com/v1.0"
      authentication:
        type: bearer
        token: "$secrets.msgraph_token"
      resources:
        - name: mail
          path: "/me/sendMail"
          operations:
            - name: send-mail
              method: POST

Retrieves purchase order, goods receipt, and invoice data from SAP to perform a three-way match verification.

naftiko: "0.5"
info:
  label: "SAP Invoice Three-Way Match"
  description: "Retrieves purchase order, goods receipt, and invoice data from SAP to perform a three-way match verification."
  tags:
    - finance
    - procurement
    - sap
capability:
  exposes:
    - type: mcp
      namespace: finance-ops
      port: 8080
      tools:
        - name: verify-three-way-match
          description: "Given a SAP invoice number, verify the PO amount, goods receipt quantity, and invoice total match."
          inputParameters:
            - name: invoice_number
              in: body
              type: string
              description: "SAP invoice document number."
          steps:
            - name: get-invoice
              type: call
              call: sap.get-invoice
              with:
                invoice_number: "{{invoice_number}}"
            - name: get-po
              type: call
              call: sap.get-po
              with:
                po_number: "{{get-invoice.po_number}}"
            - name: get-gr
              type: call
              call: sap.get-goods-receipt
              with:
                doc_number: "{{get-invoice.gr_document}}"
  consumes:
    - type: http
      namespace: sap
      baseUri: "https://elsevier-s4.sap.com/sap/opu/odata/sap"
      authentication:
        type: basic
        username: "$secrets.sap_user"
        password: "$secrets.sap_password"
      resources:
        - name: invoices
          path: "/API_SUPPLIERINVOICE_PROCESS_SRV/A_SupplierInvoice('{{invoice_number}}')"
          inputParameters:
            - name: invoice_number
              in: path
          operations:
            - name: get-invoice
              method: GET
        - name: purchase-orders
          path: "/MM_PUR_PO_MAINT_V2_SRV/A_PurchaseOrder('{{po_number}}')"
          inputParameters:
            - name: po_number
              in: path
          operations:
            - name: get-po
              method: GET
        - name: goods-receipts
          path: "/API_MATERIAL_DOCUMENT_SRV/A_MaterialDocumentHeader('{{doc_number}}')"
          inputParameters:
            - name: doc_number
              in: path
          operations:
            - name: get-goods-receipt
              method: GET

At month end, queries SAP for open accounting documents, creates a ServiceNow change request for the close checklist, and notifies the finance team via Teams.

naftiko: "0.5"
info:
  label: "SAP Period Close Checklist"
  description: "At month end, queries SAP for open accounting documents, creates a ServiceNow change request for the close checklist, and notifies the finance team via Teams."
  tags:
    - finance
    - erp
    - sap
    - servicenow
    - msteams
    - period-close
capability:
  exposes:
    - type: mcp
      namespace: finance-close
      port: 8080
      tools:
        - name: run-period-close-checklist
          description: "Given a fiscal period and company code, query SAP for open items, open a ServiceNow change request for period close, and notify the finance team in Teams. Use at the start of month-end close procedures."
          inputParameters:
            - name: fiscal_period
              in: body
              type: string
              description: "SAP fiscal period in YYYYMM format (e.g., 202503)."
            - name: company_code
              in: body
              type: string
              description: "SAP company code (e.g., ELS1)."
          steps:
            - name: get-open-items
              type: call
              call: sap.get-open-items
              with:
                fiscal_period: "{{fiscal_period}}"
                company_code: "{{company_code}}"
            - name: create-change-request
              type: call
              call: servicenow.create-change-request
              with:
                short_description: "Period Close {{fiscal_period}} — {{company_code}}"
                category: "financial_close"
                description: "Open items count: {{get-open-items.open_count}}. SAP company code: {{company_code}}"
            - name: notify-finance
              type: call
              call: msteams.send-message
              with:
                channel: "finance-close"
                text: "Period close initiated for {{fiscal_period}} / {{company_code}}. Open items: {{get-open-items.open_count}}. Change: {{create-change-request.number}}"
  consumes:
    - type: http
      namespace: sap
      baseUri: "https://elsevier-s4.sap.com/sap/opu/odata/sap/FI_PERIOD_CLOSE_SRV"
      authentication:
        type: basic
        username: "$secrets.sap_user"
        password: "$secrets.sap_password"
      resources:
        - name: open-items
          path: "/A_OpenItem"
          inputParameters:
            - name: fiscal_period
              in: query
            - name: company_code
              in: query
          operations:
            - name: get-open-items
              method: GET
    - type: http
      namespace: servicenow
      baseUri: "https://elsevier.service-now.com/api/now"
      authentication:
        type: basic
        username: "$secrets.servicenow_user"
        password: "$secrets.servicenow_password"
      resources:
        - name: change-requests
          path: "/table/change_request"
          operations:
            - name: create-change-request
              method: POST
    - type: http
      namespace: msteams
      baseUri: "https://graph.microsoft.com/v1.0"
      authentication:
        type: bearer
        token: "$secrets.msteams_token"
      resources:
        - name: channel-messages
          path: "/teams/finance/channels/general/messages"
          operations:
            - name: send-message
              method: POST

Retrieves vendor payment status from SAP by vendor number and invoice.

naftiko: "0.5"
info:
  label: "SAP Vendor Payment Status"
  description: "Retrieves vendor payment status from SAP by vendor number and invoice."
  tags:
    - finance
    - erp
    - sap
capability:
  exposes:
    - type: mcp
      namespace: finance-ops
      port: 8080
      tools:
        - name: get-payment-status
          description: "Given a SAP vendor number and invoice, return payment status and scheduled date."
          inputParameters:
            - name: vendor_number
              in: body
              type: string
              description: "SAP vendor number."
            - name: invoice_number
              in: body
              type: string
              description: "Invoice number."
          call: sap.get-payment
          with:
            vendor: "{{vendor_number}}"
            invoice: "{{invoice_number}}"
          outputParameters:
            - name: status
              type: string
              mapping: "$.d.PaymentStatus"
            - name: scheduled_date
              type: string
              mapping: "$.d.PlannedPaymentDate"
  consumes:
    - type: http
      namespace: sap
      baseUri: "https://elsevier-s4.sap.com/sap/opu/odata/sap/API_SUPPLIERINVOICE_PROCESS_SRV"
      authentication:
        type: basic
        username: "$secrets.sap_user"
        password: "$secrets.sap_password"
      resources:
        - name: payments
          path: "/A_SupplierInvoice"
          operations:
            - name: get-payment
              method: GET

Retrieves article download statistics from Snowflake for a given DOI and journal.

naftiko: "0.5"
info:
  label: "ScienceDirect Article Download Stats"
  description: "Retrieves article download statistics from Snowflake for a given DOI and journal."
  tags:
    - publishing
    - analytics
    - snowflake
capability:
  exposes:
    - type: mcp
      namespace: content-analytics
      port: 8080
      tools:
        - name: get-download-stats
          description: "Given a DOI, return total downloads, monthly trend, and top institutions from Snowflake."
          inputParameters:
            - name: doi
              in: body
              type: string
              description: "Article DOI."
          call: snowflake.execute-statement
          with:
            statement: "SELECT total_downloads, monthly_downloads, top_institutions FROM article_stats WHERE doi = '{{doi}}'"
          outputParameters:
            - name: data
              type: array
              mapping: "$.data"
  consumes:
    - type: http
      namespace: snowflake
      baseUri: "https://elsevier.snowflakecomputing.com/api/v2"
      authentication:
        type: bearer
        token: "$secrets.snowflake_token"
      resources:
        - name: statements
          path: "/statements"
          operations:
            - name: execute-statement
              method: POST

Periodically checks Scopus for newly retracted articles associated with Elsevier journals, logs them in Snowflake, and notifies the editorial team via Teams.

naftiko: "0.5"
info:
  label: "Scopus Article Retraction Monitor"
  description: "Periodically checks Scopus for newly retracted articles associated with Elsevier journals, logs them in Snowflake, and notifies the editorial team via Teams."
  tags:
    - publishing
    - compliance
    - scopus
    - snowflake
    - msteams
    - monitoring
capability:
  exposes:
    - type: mcp
      namespace: retraction-monitor
      port: 8080
      tools:
        - name: check-retractions
          description: "Given a journal ISSN and a lookback window in days, query Scopus for retracted articles, write each retraction to Snowflake for audit, and post a summary to the editorial Teams channel."
          inputParameters:
            - name: journal_issn
              in: body
              type: string
              description: "Journal ISSN to check for recent retractions."
            - name: lookback_days
              in: body
              type: integer
              description: "Number of days to look back for newly indexed retractions."
          steps:
            - name: search-retractions
              type: call
              call: scopus.search-retractions
              with:
                issn: "{{journal_issn}}"
                days: "{{lookback_days}}"
            - name: write-audit-log
              type: call
              call: snowflake.insert-retraction-log
              with:
                issn: "{{journal_issn}}"
                retraction_dois: "{{search-retractions.dois}}"
                count: "{{search-retractions.count}}"
            - name: notify-editorial
              type: call
              call: msteams.send-message
              with:
                channel: "editorial-alerts"
                text: "Retraction check for ISSN {{journal_issn}}: {{search-retractions.count}} retractions found in last {{lookback_days}} days."
  consumes:
    - type: http
      namespace: scopus
      baseUri: "https://api.elsevier.com/content/search/scopus"
      authentication:
        type: apikey
        key: "X-ELS-APIKey"
        value: "$secrets.scopus_api_key"
        placement: header
      resources:
        - name: article-search
          path: "/"
          inputParameters:
            - name: issn
              in: query
            - name: days
              in: query
          operations:
            - name: search-retractions
              method: GET
    - type: http
      namespace: snowflake
      baseUri: "https://elsevier.snowflakecomputing.com/api/v2"
      authentication:
        type: bearer
        token: "$secrets.snowflake_token"
      resources:
        - name: retraction-log
          path: "/statements"
          operations:
            - name: insert-retraction-log
              method: POST
    - type: http
      namespace: msteams
      baseUri: "https://graph.microsoft.com/v1.0"
      authentication:
        type: bearer
        token: "$secrets.msteams_token"
      resources:
        - name: channel-messages
          path: "/teams/editorial/channels/alerts/messages"
          operations:
            - name: send-message
              method: POST

Searches Scopus for an author by name and returns matching author profiles with publication counts.

naftiko: "0.5"
info:
  label: "Scopus Author Search"
  description: "Searches Scopus for an author by name and returns matching author profiles with publication counts."
  tags:
    - research
    - publishing
    - scopus
capability:
  exposes:
    - type: mcp
      namespace: research-ops
      port: 8080
      tools:
        - name: search-author
          description: "Given an author name, search Scopus and return matching profiles with h-index and document count."
          inputParameters:
            - name: author_name
              in: body
              type: string
              description: "Author name to search."
          call: scopus.search-author
          with:
            query: "AUTHNAME({{author_name}})"
          outputParameters:
            - name: authors
              type: array
              mapping: "$.search-results.entry"
  consumes:
    - type: http
      namespace: scopus
      baseUri: "https://api.elsevier.com/content"
      authentication:
        type: apikey
        key: "X-ELS-APIKey"
        value: "$secrets.scopus_api_key"
        placement: header
      resources:
        - name: author-search
          path: "/search/author"
          operations:
            - name: search-author
              method: GET

Retrieves the citation count for an article in Scopus by DOI, returning total citations and h-index context.

naftiko: "0.5"
info:
  label: "Scopus Citation Count Lookup"
  description: "Retrieves the citation count for an article in Scopus by DOI, returning total citations and h-index context."
  tags:
    - publishing
    - research
    - scopus
capability:
  exposes:
    - type: mcp
      namespace: research-ops
      port: 8080
      tools:
        - name: get-citation-count
          description: "Given an article DOI, return the total citation count from Scopus."
          inputParameters:
            - name: doi
              in: body
              type: string
              description: "Article DOI."
          call: scopus.get-citations
          with:
            doi: "{{doi}}"
          outputParameters:
            - name: citation_count
              type: integer
              mapping: "$.search-results.entry[0].citedby-count"
            - name: title
              type: string
              mapping: "$.search-results.entry[0].dc:title"
  consumes:
    - type: http
      namespace: scopus
      baseUri: "https://api.elsevier.com/content"
      authentication:
        type: apikey
        key: "X-ELS-APIKey"
        value: "$secrets.scopus_api_key"
        placement: header
      resources:
        - name: search
          path: "/search/scopus"
          operations:
            - name: get-citations
              method: GET

Sends a transactional email to an author via SendGrid for manuscript status notifications.

naftiko: "0.5"
info:
  label: "SendGrid Author Email Delivery"
  description: "Sends a transactional email to an author via SendGrid for manuscript status notifications."
  tags:
    - communications
    - publishing
    - sendgrid
capability:
  exposes:
    - type: mcp
      namespace: comms-ops
      port: 8080
      tools:
        - name: send-author-email
          description: "Given an author email, subject, and body, send a transactional email via SendGrid."
          inputParameters:
            - name: to_email
              in: body
              type: string
              description: "Author email address."
            - name: subject
              in: body
              type: string
              description: "Email subject."
            - name: body
              in: body
              type: string
              description: "Email body."
          call: sendgrid.send-email
          with:
            to: "{{to_email}}"
            subject: "{{subject}}"
            content: "{{body}}"
          outputParameters:
            - name: status_code
              type: integer
              mapping: "$.statusCode"
  consumes:
    - type: http
      namespace: sendgrid
      baseUri: "https://api.sendgrid.com/v3"
      authentication:
        type: bearer
        token: "$secrets.sendgrid_api_key"
      resources:
        - name: mail
          path: "/mail/send"
          operations:
            - name: send-email
              method: POST

Looks up a ServiceNow change request, validates it against the CAB schedule, and notifies the change owner via Teams with approval status.

naftiko: "0.5"
info:
  label: "ServiceNow Change Request Approval"
  description: "Looks up a ServiceNow change request, validates it against the CAB schedule, and notifies the change owner via Teams with approval status."
  tags:
    - itsm
    - change-management
    - servicenow
    - msteams
    - approval
capability:
  exposes:
    - type: mcp
      namespace: change-management
      port: 8080
      tools:
        - name: get-change-request-status
          description: "Given a ServiceNow change request number, fetch its current state, approval status, and scheduled window, then send a Teams summary to the change owner. Use for CAB preparation and stakeholder updates."
          inputParameters:
            - name: change_number
              in: body
              type: string
              description: "ServiceNow change request number (e.g., CHG0012345)."
          steps:
            - name: get-change
              type: call
              call: servicenow.get-change-request
              with:
                number: "{{change_number}}"
            - name: notify-owner
              type: call
              call: msteams.send-message
              with:
                channel: "change-management"
                text: "Change {{change_number}} status: {{get-change.state}} | Approval: {{get-change.approval}} | Window: {{get-change.start_date}} to {{get-change.end_date}}"
  consumes:
    - type: http
      namespace: servicenow
      baseUri: "https://elsevier.service-now.com/api/now"
      authentication:
        type: basic
        username: "$secrets.servicenow_user"
        password: "$secrets.servicenow_password"
      resources:
        - name: change-requests
          path: "/table/change_request"
          inputParameters:
            - name: number
              in: query
          operations:
            - name: get-change-request
              method: GET
    - type: http
      namespace: msteams
      baseUri: "https://graph.microsoft.com/v1.0"
      authentication:
        type: bearer
        token: "$secrets.msteams_token"
      resources:
        - name: channel-messages
          path: "/teams/it/channels/change-management/messages"
          operations:
            - name: send-message
              method: POST

When a P1 ServiceNow incident is created, creates a corresponding Jira engineering ticket and notifies Microsoft Teams.

naftiko: "0.5"
info:
  label: "ServiceNow Incident to Jira"
  description: "When a P1 ServiceNow incident is created, creates a corresponding Jira engineering ticket and notifies Microsoft Teams."
  tags:
    - it-operations
    - incident-management
    - servicenow
    - jira
    - microsoft-teams
capability:
  exposes:
    - type: mcp
      namespace: incident-ops
      port: 8080
      tools:
        - name: escalate-to-engineering
          description: "Given a ServiceNow incident number, create a Jira ticket and notify Teams."
          inputParameters:
            - name: incident_number
              in: body
              type: string
              description: "ServiceNow incident number."
          steps:
            - name: get-incident
              type: call
              call: servicenow.get-incident
              with:
                number: "{{incident_number}}"
            - name: create-jira
              type: call
              call: jira.create-issue
              with:
                project_key: "ENG"
                issuetype: "Bug"
                summary: "SNOW {{incident_number}}: {{get-incident.short_description}}"
            - name: notify-teams
              type: call
              call: msteams.post-channel-message
              with:
                channel_id: "engineering-incidents"
                text: "P1 escalated: {{incident_number}} | Jira: {{create-jira.key}}"
  consumes:
    - type: http
      namespace: servicenow
      baseUri: "https://elsevier.service-now.com/api/now"
      authentication:
        type: basic
        username: "$secrets.servicenow_user"
        password: "$secrets.servicenow_password"
      resources:
        - name: incidents
          path: "/table/incident"
          operations:
            - name: get-incident
              method: GET
    - type: http
      namespace: jira
      baseUri: "https://elsevier.atlassian.net/rest/api/3"
      authentication:
        type: basic
        username: "$secrets.jira_user"
        password: "$secrets.jira_token"
      resources:
        - name: issues
          path: "/issue"
          operations:
            - name: create-issue
              method: POST
    - type: http
      namespace: msteams
      baseUri: "https://graph.microsoft.com/v1.0"
      authentication:
        type: bearer
        token: "$secrets.msteams_token"
      resources:
        - name: channel-messages
          path: "/teams/{{team_id}}/channels/{{channel_id}}/messages"
          inputParameters:
            - name: channel_id
              in: path
          operations:
            - name: post-channel-message
              method: POST

Retrieves the status of a ServiceNow IT service request by request number.

naftiko: "0.5"
info:
  label: "ServiceNow IT Request Status"
  description: "Retrieves the status of a ServiceNow IT service request by request number."
  tags:
    - it-operations
    - support
    - servicenow
capability:
  exposes:
    - type: mcp
      namespace: it-ops
      port: 8080
      tools:
        - name: get-request-status
          description: "Given a ServiceNow request number, return the current state, assigned group, and expected resolution date."
          inputParameters:
            - name: request_number
              in: body
              type: string
              description: "ServiceNow request number."
          call: servicenow.get-request
          with:
            number: "{{request_number}}"
          outputParameters:
            - name: state
              type: string
              mapping: "$.result.state"
            - name: assigned_group
              type: string
              mapping: "$.result.assignment_group.display_value"
  consumes:
    - type: http
      namespace: servicenow
      baseUri: "https://elsevier.service-now.com/api/now"
      authentication:
        type: basic
        username: "$secrets.servicenow_user"
        password: "$secrets.servicenow_password"
      resources:
        - name: requests
          path: "/table/sc_request"
          operations:
            - name: get-request
              method: GET

Searches a SharePoint document library by keyword and returns matching files.

naftiko: "0.5"
info:
  label: "SharePoint Document Library Search"
  description: "Searches a SharePoint document library by keyword and returns matching files."
  tags:
    - collaboration
    - document-management
    - sharepoint
capability:
  exposes:
    - type: mcp
      namespace: doc-ops
      port: 8080
      tools:
        - name: search-documents
          description: "Given a SharePoint site ID and search keyword, return matching documents."
          inputParameters:
            - name: site_id
              in: body
              type: string
              description: "SharePoint site ID."
            - name: keyword
              in: body
              type: string
              description: "Search keyword."
          call: sharepoint.search
          with:
            site_id: "{{site_id}}"
            query: "{{keyword}}"
          outputParameters:
            - name: files
              type: array
              mapping: "$.value"
  consumes:
    - type: http
      namespace: sharepoint
      baseUri: "https://graph.microsoft.com/v1.0"
      authentication:
        type: bearer
        token: "$secrets.msgraph_token"
      resources:
        - name: search
          path: "/sites/{{site_id}}/drive/root/search(q='{{query}}')"
          inputParameters:
            - name: site_id
              in: path
            - name: query
              in: path
          operations:
            - name: search
              method: GET

Posts a daily standup prompt to the editorial Slack channel and collects responses for the team lead.

naftiko: "0.5"
info:
  label: "Slack Editorial Standup Bot"
  description: "Posts a daily standup prompt to the editorial Slack channel and collects responses for the team lead."
  tags:
    - publishing
    - collaboration
    - slack
capability:
  exposes:
    - type: mcp
      namespace: editorial-ops
      port: 8080
      tools:
        - name: post-standup-prompt
          description: "Post a daily standup prompt to the editorial Slack channel with manuscript pipeline summary."
          inputParameters:
            - name: slack_channel
              in: body
              type: string
              description: "Slack channel for standup."
            - name: pipeline_summary
              in: body
              type: string
              description: "Current manuscript pipeline summary text."
          call: slack.post-message
          with:
            channel: "{{slack_channel}}"
            text: "Daily Editorial Standup\nPipeline: {{pipeline_summary}}\nPlease share your updates."
          outputParameters:
            - name: message_ts
              type: string
              mapping: "$.ts"
  consumes:
    - type: http
      namespace: slack
      baseUri: "https://slack.com/api"
      authentication:
        type: bearer
        token: "$secrets.slack_bot_token"
      resources:
        - name: messages
          path: "/chat.postMessage"
          operations:
            - name: post-message
              method: POST

Queries Snowflake for weekly publishing metrics and posts a digest to the leadership Slack channel.

naftiko: "0.5"
info:
  label: "Slack Weekly Publishing Digest"
  description: "Queries Snowflake for weekly publishing metrics and posts a digest to the leadership Slack channel."
  tags:
    - publishing
    - analytics
    - snowflake
    - slack
capability:
  exposes:
    - type: mcp
      namespace: publishing-ops
      port: 8080
      tools:
        - name: post-publishing-digest
          description: "Fetch weekly publishing metrics from Snowflake and post to the leadership Slack channel."
          inputParameters:
            - name: slack_channel
              in: body
              type: string
              description: "Slack channel."
          steps:
            - name: fetch-metrics
              type: call
              call: snowflake.execute-statement
              with:
                statement: "SELECT articles_published, reviews_completed, avg_review_time FROM weekly_metrics WHERE week_ending = CURRENT_DATE"
            - name: post-digest
              type: call
              call: slack.post-message
              with:
                channel: "{{slack_channel}}"
                text: "Weekly publishing digest: {{fetch-metrics.data}}"
  consumes:
    - type: http
      namespace: snowflake
      baseUri: "https://elsevier.snowflakecomputing.com/api/v2"
      authentication:
        type: bearer
        token: "$secrets.snowflake_token"
      resources:
        - name: statements
          path: "/statements"
          operations:
            - name: execute-statement
              method: POST
    - type: http
      namespace: slack
      baseUri: "https://slack.com/api"
      authentication:
        type: bearer
        token: "$secrets.slack_bot_token"
      resources:
        - name: messages
          path: "/chat.postMessage"
          operations:
            - name: post-message
              method: POST

Checks Snowflake query history for failed or long-running jobs, logs anomalies to Datadog, and opens a Jira task for the data engineering team.

naftiko: "0.5"
info:
  label: "Snowflake Data Pipeline Monitor"
  description: "Checks Snowflake query history for failed or long-running jobs, logs anomalies to Datadog, and opens a Jira task for the data engineering team."
  tags:
    - data-engineering
    - analytics
    - snowflake
    - datadog
    - jira
    - monitoring
capability:
  exposes:
    - type: mcp
      namespace: data-ops
      port: 8080
      tools:
        - name: monitor-snowflake-pipelines
          description: "Given a Snowflake warehouse name and a failure threshold in minutes, query job history for failed or stalled jobs, send a metric to Datadog, and open a Jira task if failures are found. Use for daily pipeline health checks."
          inputParameters:
            - name: warehouse_name
              in: body
              type: string
              description: "Snowflake virtual warehouse name to inspect (e.g., ANALYTICS_WH)."
            - name: failure_threshold_minutes
              in: body
              type: integer
              description: "Minimum run time in minutes to flag a query as stalled."
          steps:
            - name: query-job-history
              type: call
              call: snowflake.get-query-history
              with:
                warehouse: "{{warehouse_name}}"
                threshold_minutes: "{{failure_threshold_minutes}}"
            - name: post-metric
              type: call
              call: datadog.submit-metric
              with:
                metric: "elsevier.snowflake.failed_jobs"
                points: "{{query-job-history.failure_count}}"
                tags: "warehouse:{{warehouse_name}}"
            - name: open-jira-task
              type: call
              call: jira.create-issue
              with:
                project_key: "DATA"
                issuetype: "Task"
                summary: "Snowflake pipeline failures in {{warehouse_name}}: {{query-job-history.failure_count}} jobs"
                description: "Failed jobs detected: {{query-job-history.failed_job_names}}. Threshold: {{failure_threshold_minutes}} min."
  consumes:
    - type: http
      namespace: snowflake
      baseUri: "https://elsevier.snowflakecomputing.com/api/v2"
      authentication:
        type: bearer
        token: "$secrets.snowflake_token"
      resources:
        - name: query-history
          path: "/statements"
          inputParameters:
            - name: warehouse
              in: query
            - name: threshold_minutes
              in: query
          operations:
            - name: get-query-history
              method: POST
    - type: http
      namespace: datadog
      baseUri: "https://api.datadoghq.com/api/v1"
      authentication:
        type: apikey
        key: "DD-API-KEY"
        value: "$secrets.datadog_api_key"
        placement: header
      resources:
        - name: metrics
          path: "/series"
          operations:
            - name: submit-metric
              method: POST
    - type: http
      namespace: jira
      baseUri: "https://elsevier.atlassian.net/rest/api/3"
      authentication:
        type: basic
        username: "$secrets.jira_user"
        password: "$secrets.jira_api_token"
      resources:
        - name: issues
          path: "/issue"
          operations:
            - name: create-issue
              method: POST

Executes a read-only SQL query against the Snowflake content usage analytics warehouse.

naftiko: "0.5"
info:
  label: "Snowflake Usage Analytics Query"
  description: "Executes a read-only SQL query against the Snowflake content usage analytics warehouse."
  tags:
    - data
    - analytics
    - snowflake
capability:
  exposes:
    - type: mcp
      namespace: data-ops
      port: 8080
      tools:
        - name: run-usage-query
          description: "Given a SQL query, execute it against the Snowflake usage analytics warehouse and return results."
          inputParameters:
            - name: sql
              in: body
              type: string
              description: "Read-only SQL query."
          call: snowflake.execute-statement
          with:
            statement: "{{sql}}"
            warehouse: "USAGE_ANALYTICS_WH"
          outputParameters:
            - name: rows
              type: array
              mapping: "$.data"
            - name: row_count
              type: integer
              mapping: "$.resultSetMetaData.numRows"
  consumes:
    - type: http
      namespace: snowflake
      baseUri: "https://elsevier.snowflakecomputing.com/api/v2"
      authentication:
        type: bearer
        token: "$secrets.snowflake_token"
      resources:
        - name: statements
          path: "/statements"
          operations:
            - name: execute-statement
              method: POST

Runs a Splunk saved search for security events and creates a ServiceNow incident if critical findings exist.

naftiko: "0.5"
info:
  label: "Splunk Security Log Search"
  description: "Runs a Splunk saved search for security events and creates a ServiceNow incident if critical findings exist."
  tags:
    - security
    - siem
    - splunk
    - servicenow
capability:
  exposes:
    - type: mcp
      namespace: security-ops
      port: 8080
      tools:
        - name: search-security-logs
          description: "Run a Splunk saved search and create ServiceNow incidents for critical findings."
          inputParameters:
            - name: search_name
              in: body
              type: string
              description: "Splunk saved search name."
          steps:
            - name: run-search
              type: call
              call: splunk.dispatch-search
              with:
                name: "{{search_name}}"
            - name: create-incident
              type: call
              call: servicenow.create-incident
              with:
                short_description: "Splunk finding: {{search_name}}"
                category: "Security"
                impact: "1"
  consumes:
    - type: http
      namespace: splunk
      baseUri: "https://elsevier-splunk.splunkcloud.com:8089/servicesNS/admin/search"
      authentication:
        type: bearer
        token: "$secrets.splunk_token"
      resources:
        - name: saved-searches
          path: "/saved/searches/{{name}}/dispatch"
          inputParameters:
            - name: name
              in: path
          operations:
            - name: dispatch-search
              method: POST
    - type: http
      namespace: servicenow
      baseUri: "https://elsevier.service-now.com/api/now"
      authentication:
        type: basic
        username: "$secrets.servicenow_user"
        password: "$secrets.servicenow_password"
      resources:
        - name: incidents
          path: "/table/incident"
          operations:
            - name: create-incident
              method: POST

Queries Salesforce for institutional subscriptions expiring within 90 days, enriches contacts via Scopus, and triggers a HubSpot renewal email campaign.

naftiko: "0.5"
info:
  label: "Subscription Renewal Campaign"
  description: "Queries Salesforce for institutional subscriptions expiring within 90 days, enriches contacts via Scopus, and triggers a HubSpot renewal email campaign."
  tags:
    - sales
    - crm
    - marketing
    - salesforce
    - hubspot
    - scopus
    - renewal
capability:
  exposes:
    - type: mcp
      namespace: renewal-marketing
      port: 8080
      tools:
        - name: trigger-renewal-campaign
          description: "Given a journal product code and renewal window in days, query Salesforce for expiring subscriptions, and enroll matching contacts in a HubSpot renewal workflow. Use 90 days before subscription end date."
          inputParameters:
            - name: product_code
              in: body
              type: string
              description: "Journal bundle product code (e.g., FREEDOM-COLLECTION-2026)."
            - name: days_until_expiry
              in: body
              type: integer
              description: "Number of days before expiry to target (e.g., 90)."
          steps:
            - name: get-expiring-subscriptions
              type: call
              call: salesforce.query-expiring-subscriptions
              with:
                product_code: "{{product_code}}"
                days_until_expiry: "{{days_until_expiry}}"
            - name: enroll-hubspot
              type: call
              call: hubspot.enroll-contacts-in-workflow
              with:
                workflow_id: "$secrets.hubspot_renewal_workflow_id"
                contact_emails: "{{get-expiring-subscriptions.contact_emails}}"
  consumes:
    - type: http
      namespace: salesforce
      baseUri: "https://elsevier.my.salesforce.com/services/data/v58.0"
      authentication:
        type: bearer
        token: "$secrets.salesforce_token"
      resources:
        - name: subscriptions
          path: "/query"
          inputParameters:
            - name: product_code
              in: query
            - name: days_until_expiry
              in: query
          operations:
            - name: query-expiring-subscriptions
              method: GET
    - type: http
      namespace: hubspot
      baseUri: "https://api.hubapi.com/marketing/v3"
      authentication:
        type: bearer
        token: "$secrets.hubspot_token"
      resources:
        - name: workflow-enrollments
          path: "/workflows/{{workflow_id}}/enrollments"
          inputParameters:
            - name: workflow_id
              in: path
          operations:
            - name: enroll-contacts-in-workflow
              method: POST

Triggers a Tableau Server extract refresh for the journal metrics dashboard and notifies the editorial team via Microsoft Teams.

naftiko: "0.5"
info:
  label: "Tableau Journal Metrics Refresh"
  description: "Triggers a Tableau Server extract refresh for the journal metrics dashboard and notifies the editorial team via Microsoft Teams."
  tags:
    - analytics
    - publishing
    - tableau
    - microsoft-teams
capability:
  exposes:
    - type: mcp
      namespace: bi-ops
      port: 8080
      tools:
        - name: refresh-journal-metrics
          description: "Trigger a Tableau workbook refresh for journal metrics and notify editorial via Teams."
          inputParameters:
            - name: workbook_id
              in: body
              type: string
              description: "Tableau workbook ID."
          steps:
            - name: trigger-refresh
              type: call
              call: tableau.refresh-workbook
              with:
                workbook_id: "{{workbook_id}}"
            - name: notify-editorial
              type: call
              call: msteams.post-channel-message
              with:
                channel_id: "editorial-analytics"
                text: "Journal metrics dashboard refresh triggered. Job: {{trigger-refresh.job.id}}"
  consumes:
    - type: http
      namespace: tableau
      baseUri: "https://tableau.elsevier.com/api/3.21"
      authentication:
        type: bearer
        token: "$secrets.tableau_token"
      resources:
        - name: workbooks
          path: "/sites/{{site_id}}/workbooks/{{workbook_id}}/refresh"
          inputParameters:
            - name: workbook_id
              in: path
          operations:
            - name: refresh-workbook
              method: POST
    - type: http
      namespace: msteams
      baseUri: "https://graph.microsoft.com/v1.0"
      authentication:
        type: bearer
        token: "$secrets.msteams_token"
      resources:
        - name: channel-messages
          path: "/teams/{{team_id}}/channels/{{channel_id}}/messages"
          inputParameters:
            - name: channel_id
              in: path
          operations:
            - name: post-channel-message
              method: POST

Queries Tableau Server for dashboard view counts by department, aggregates in Snowflake, and sends a weekly digest report to stakeholders via Microsoft Teams.

naftiko: "0.5"
info:
  label: "Tableau Usage Digest"
  description: "Queries Tableau Server for dashboard view counts by department, aggregates in Snowflake, and sends a weekly digest report to stakeholders via Microsoft Teams."
  tags:
    - analytics
    - reporting
    - tableau
    - snowflake
    - msteams
capability:
  exposes:
    - type: mcp
      namespace: analytics-reporting
      port: 8080
      tools:
        - name: digest-tableau-usage
          description: "Given a Tableau site name and date range, fetch workbook view stats, write aggregated counts to Snowflake, and post a usage digest to the analytics Teams channel. Use for weekly BI adoption reporting."
          inputParameters:
            - name: tableau_site
              in: body
              type: string
              description: "Tableau Server site name (e.g., elsevier-analytics)."
            - name: start_date
              in: body
              type: string
              description: "Report start date in ISO 8601 format (YYYY-MM-DD)."
            - name: end_date
              in: body
              type: string
              description: "Report end date in ISO 8601 format (YYYY-MM-DD)."
          steps:
            - name: get-view-stats
              type: call
              call: tableau.get-view-stats
              with:
                site: "{{tableau_site}}"
                start_date: "{{start_date}}"
                end_date: "{{end_date}}"
            - name: write-to-snowflake
              type: call
              call: snowflake.insert-usage-row
              with:
                site: "{{tableau_site}}"
                total_views: "{{get-view-stats.total_views}}"
                start_date: "{{start_date}}"
                end_date: "{{end_date}}"
            - name: post-digest
              type: call
              call: msteams.send-message
              with:
                channel: "analytics-ops"
                text: "Tableau Usage {{start_date}} to {{end_date}}: {{get-view-stats.total_views}} total views across {{get-view-stats.workbook_count}} workbooks."
  consumes:
    - type: http
      namespace: tableau
      baseUri: "https://elsevier-tableau.online.tableau.com/api/2.8"
      authentication:
        type: bearer
        token: "$secrets.tableau_token"
      resources:
        - name: view-stats
          path: "/sites/{{site}}/views/usage"
          inputParameters:
            - name: site
              in: path
            - name: start_date
              in: query
            - name: end_date
              in: query
          operations:
            - name: get-view-stats
              method: GET
    - type: http
      namespace: snowflake
      baseUri: "https://elsevier.snowflakecomputing.com/api/v2"
      authentication:
        type: bearer
        token: "$secrets.snowflake_token"
      resources:
        - name: usage-table
          path: "/statements"
          operations:
            - name: insert-usage-row
              method: POST
    - type: http
      namespace: msteams
      baseUri: "https://graph.microsoft.com/v1.0"
      authentication:
        type: bearer
        token: "$secrets.msteams_token"
      resources:
        - name: channel-messages
          path: "/teams/analytics/channels/general/messages"
          operations:
            - name: send-message
              method: POST

Triggers a Terraform Cloud apply run for infrastructure provisioning and notifies the infra team via Slack.

naftiko: "0.5"
info:
  label: "Terraform Infrastructure Provisioning"
  description: "Triggers a Terraform Cloud apply run for infrastructure provisioning and notifies the infra team via Slack."
  tags:
    - infrastructure
    - iac
    - terraform
    - slack
capability:
  exposes:
    - type: mcp
      namespace: infra-ops
      port: 8080
      tools:
        - name: provision-infrastructure
          description: "Given a Terraform workspace ID, trigger an apply run and notify Slack."
          inputParameters:
            - name: workspace_id
              in: body
              type: string
              description: "Terraform Cloud workspace ID."
          steps:
            - name: create-run
              type: call
              call: terraform.create-run
              with:
                workspace_id: "{{workspace_id}}"
            - name: notify-infra
              type: call
              call: slack.post-message
              with:
                channel: "infra-ops"
                text: "Terraform run triggered: workspace {{workspace_id}}, run {{create-run.id}}"
  consumes:
    - type: http
      namespace: terraform
      baseUri: "https://app.terraform.io/api/v2"
      authentication:
        type: bearer
        token: "$secrets.terraform_token"
      resources:
        - name: runs
          path: "/runs"
          operations:
            - name: create-run
              method: POST
    - type: http
      namespace: slack
      baseUri: "https://slack.com/api"
      authentication:
        type: bearer
        token: "$secrets.slack_bot_token"
      resources:
        - name: messages
          path: "/chat.postMessage"
          operations:
            - name: post-message
              method: POST

Retrieves an employee's PTO and sick leave balances from Workday by employee ID.

naftiko: "0.5"
info:
  label: "Workday Absence Balance Lookup"
  description: "Retrieves an employee's PTO and sick leave balances from Workday by employee ID."
  tags:
    - hr
    - leave-management
    - workday
capability:
  exposes:
    - type: mcp
      namespace: hr-ops
      port: 8080
      tools:
        - name: get-absence-balance
          description: "Given a Workday employee ID, return PTO balance, sick leave balance, and next accrual date."
          inputParameters:
            - name: employee_id
              in: body
              type: string
              description: "Workday employee ID."
          call: workday.get-balance
          with:
            employee_id: "{{employee_id}}"
          outputParameters:
            - name: pto_balance
              type: number
              mapping: "$.Worker.TimeOff.PTO_Balance"
            - name: sick_balance
              type: number
              mapping: "$.Worker.TimeOff.Sick_Balance"
  consumes:
    - type: http
      namespace: workday
      baseUri: "https://wd5-impl-services1.workday.com/ccx/service/elsevier"
      authentication:
        type: basic
        username: "$secrets.workday_user"
        password: "$secrets.workday_password"
      resources:
        - name: absence
          path: "/Human_Resources/v40.0/Get_Workers"
          operations:
            - name: get-balance
              method: GET

When an employee submits an absence request in Workday exceeding five days, notifies the direct manager via Teams and creates a coverage task in ServiceNow.

naftiko: "0.5"
info:
  label: "Workday Absence Request Notification"
  description: "When an employee submits an absence request in Workday exceeding five days, notifies the direct manager via Teams and creates a coverage task in ServiceNow."
  tags:
    - hr
    - absence-management
    - workday
    - servicenow
    - msteams
capability:
  exposes:
    - type: mcp
      namespace: hr-absence
      port: 8080
      tools:
        - name: handle-absence-request
          description: "Given a Workday worker ID and absence request ID, fetch the request details, notify the manager via Teams, and open a ServiceNow task for coverage planning if the absence exceeds five days."
          inputParameters:
            - name: workday_worker_id
              in: body
              type: string
              description: "Workday worker ID of the employee requesting absence."
            - name: absence_request_id
              in: body
              type: string
              description: "Workday absence request ID."
          steps:
            - name: get-absence-details
              type: call
              call: workday.get-absence-request
              with:
                worker_id: "{{workday_worker_id}}"
                request_id: "{{absence_request_id}}"
            - name: notify-manager
              type: call
              call: msteams.send-message
              with:
                recipient_upn: "{{get-absence-details.manager_email}}"
                text: "Absence request submitted by {{get-absence-details.employee_name}}: {{get-absence-details.start_date}} to {{get-absence-details.end_date}} ({{get-absence-details.days}} days)."
            - name: create-coverage-task
              type: call
              call: servicenow.create-task
              with:
                short_description: "Coverage planning: {{get-absence-details.employee_name}} absent {{get-absence-details.start_date}} to {{get-absence-details.end_date}}"
                assignment_group: "{{get-absence-details.department}}_Managers"
  consumes:
    - type: http
      namespace: workday
      baseUri: "https://wd2-impl-services1.workday.com/ccx/api/v1"
      authentication:
        type: bearer
        token: "$secrets.workday_token"
      resources:
        - name: absence-requests
          path: "/workers/{{worker_id}}/absenceRequests/{{request_id}}"
          inputParameters:
            - name: worker_id
              in: path
            - name: request_id
              in: path
          operations:
            - name: get-absence-request
              method: GET
    - type: http
      namespace: msteams
      baseUri: "https://graph.microsoft.com/v1.0"
      authentication:
        type: bearer
        token: "$secrets.msteams_token"
      resources:
        - name: messages
          path: "/users/{{recipient_upn}}/sendMail"
          inputParameters:
            - name: recipient_upn
              in: path
          operations:
            - name: send-message
              method: POST
    - type: http
      namespace: servicenow
      baseUri: "https://elsevier.service-now.com/api/now"
      authentication:
        type: basic
        username: "$secrets.servicenow_user"
        password: "$secrets.servicenow_password"
      resources:
        - name: tasks
          path: "/table/task"
          operations:
            - name: create-task
              method: POST

Queries Workday for employees with incomplete benefits enrollment and sends reminders via Microsoft Graph email.

naftiko: "0.5"
info:
  label: "Workday Benefits Enrollment"
  description: "Queries Workday for employees with incomplete benefits enrollment and sends reminders via Microsoft Graph email."
  tags:
    - hr
    - benefits
    - workday
    - microsoft-teams
capability:
  exposes:
    - type: mcp
      namespace: hr-ops
      port: 8080
      tools:
        - name: send-enrollment-reminders
          description: "Fetch employees with pending enrollment and send reminder emails."
          inputParameters:
            - name: enrollment_period
              in: body
              type: string
              description: "Enrollment period identifier."
          steps:
            - name: get-pending
              type: call
              call: workday.get-pending-enrollment
              with:
                period: "{{enrollment_period}}"
            - name: send-reminders
              type: call
              call: msgraph.send-mail
              with:
                recipients: "{{get-pending.employee_emails}}"
                subject: "Complete Your Benefits Enrollment"
                body: "Please complete enrollment for {{enrollment_period}} in Workday."
  consumes:
    - type: http
      namespace: workday
      baseUri: "https://wd5-impl-services1.workday.com/ccx/service/elsevier"
      authentication:
        type: basic
        username: "$secrets.workday_user"
        password: "$secrets.workday_password"
      resources:
        - name: benefits
          path: "/Benefits/v40.0"
          operations:
            - name: get-pending-enrollment
              method: GET
    - type: http
      namespace: msgraph
      baseUri: "https://graph.microsoft.com/v1.0"
      authentication:
        type: bearer
        token: "$secrets.msgraph_token"
      resources:
        - name: mail
          path: "/me/sendMail"
          operations:
            - name: send-mail
              method: POST

When a compensation change is approved, notifies HR via Microsoft Teams.

naftiko: "0.5"
info:
  label: "Workday Compensation Alert"
  description: "When a compensation change is approved, notifies HR via Microsoft Teams."
  tags:
    - hr
    - compensation
    - workday
    - microsoft-teams
capability:
  exposes:
    - type: mcp
      namespace: hr-ops
      port: 8080
      tools:
        - name: notify-comp-change
          description: "Given a Workday employee ID and event ID, fetch details and notify HR via Teams."
          inputParameters:
            - name: employee_id
              in: body
              type: string
              description: "Workday employee ID."
            - name: event_id
              in: body
              type: string
              description: "Compensation event ID."
          steps:
            - name: get-event
              type: call
              call: workday.get-comp-event
              with:
                employee_id: "{{employee_id}}"
                event_id: "{{event_id}}"
            - name: notify-hr
              type: call
              call: msteams.post-channel-message
              with:
                channel_id: "hr-notifications"
                text: "Compensation change for {{employee_id}}: effective {{get-event.effective_date}}"
  consumes:
    - type: http
      namespace: workday
      baseUri: "https://wd5-impl-services1.workday.com/ccx/service/elsevier"
      authentication:
        type: basic
        username: "$secrets.workday_user"
        password: "$secrets.workday_password"
      resources:
        - name: compensation
          path: "/Compensation/v40.0"
          operations:
            - name: get-comp-event
              method: GET
    - type: http
      namespace: msteams
      baseUri: "https://graph.microsoft.com/v1.0"
      authentication:
        type: bearer
        token: "$secrets.msteams_token"
      resources:
        - name: channel-messages
          path: "/teams/{{team_id}}/channels/{{channel_id}}/messages"
          inputParameters:
            - name: channel_id
              in: path
          operations:
            - name: post-channel-message
              method: POST

Exports a headcount snapshot by department from Workday and writes the data to Snowflake for reporting and workforce planning analysis.

naftiko: "0.5"
info:
  label: "Workday Headcount Snapshot"
  description: "Exports a headcount snapshot by department from Workday and writes the data to Snowflake for reporting and workforce planning analysis."
  tags:
    - hr
    - analytics
    - workday
    - snowflake
    - reporting
    - headcount
capability:
  exposes:
    - type: mcp
      namespace: hr-analytics
      port: 8080
      tools:
        - name: snapshot-headcount
          description: "Fetch current active headcount grouped by department and cost center from Workday, then insert a snapshot row to Snowflake. Use for monthly workforce planning data updates."
          inputParameters:
            - name: snapshot_date
              in: body
              type: string
              description: "Snapshot date in ISO 8601 format (YYYY-MM-DD) to tag the record."
          steps:
            - name: export-workers
              type: call
              call: workday.export-workers
              with:
                status: "active"
                fields: "employee_id,full_name,department,cost_center,employment_type"
            - name: write-snapshot
              type: call
              call: snowflake.insert-headcount-snapshot
              with:
                snapshot_date: "{{snapshot_date}}"
                records: "{{export-workers.rows}}"
  consumes:
    - type: http
      namespace: workday
      baseUri: "https://wd2-impl-services1.workday.com/ccx/api/v1"
      authentication:
        type: bearer
        token: "$secrets.workday_token"
      resources:
        - name: workers-export
          path: "/workers"
          inputParameters:
            - name: status
              in: query
            - name: fields
              in: query
          operations:
            - name: export-workers
              method: GET
    - type: http
      namespace: snowflake
      baseUri: "https://elsevier.snowflakecomputing.com/api/v2"
      authentication:
        type: bearer
        token: "$secrets.snowflake_token"
      resources:
        - name: headcount-snapshots
          path: "/statements"
          operations:
            - name: insert-headcount-snapshot
              method: POST

Creates a position requisition in Workday and notifies the recruiting team via Slack.

naftiko: "0.5"
info:
  label: "Workday New Position Requisition"
  description: "Creates a position requisition in Workday and notifies the recruiting team via Slack."
  tags:
    - hr
    - recruiting
    - workday
    - slack
capability:
  exposes:
    - type: mcp
      namespace: recruiting-ops
      port: 8080
      tools:
        - name: create-requisition
          description: "Create a Workday position requisition and notify the recruiting Slack channel."
          inputParameters:
            - name: job_title
              in: body
              type: string
              description: "Job title."
            - name: department
              in: body
              type: string
              description: "Department."
          steps:
            - name: create-req
              type: call
              call: workday.create-requisition
              with:
                title: "{{job_title}}"
                department: "{{department}}"
            - name: notify-recruiting
              type: call
              call: slack.post-message
              with:
                channel: "recruiting"
                text: "New requisition: {{job_title}} in {{department}} | ID: {{create-req.requisition_id}}"
  consumes:
    - type: http
      namespace: workday
      baseUri: "https://wd5-impl-services1.workday.com/ccx/service/elsevier"
      authentication:
        type: basic
        username: "$secrets.workday_user"
        password: "$secrets.workday_password"
      resources:
        - name: requisitions
          path: "/Recruiting/v40.0"
          operations:
            - name: create-requisition
              method: POST
    - type: http
      namespace: slack
      baseUri: "https://slack.com/api"
      authentication:
        type: bearer
        token: "$secrets.slack_bot_token"
      resources:
        - name: messages
          path: "/chat.postMessage"
          operations:
            - name: post-message
              method: POST

Retrieves manager and direct reports for a Workday employee by employee ID.

naftiko: "0.5"
info:
  label: "Workday Org Chart Lookup"
  description: "Retrieves manager and direct reports for a Workday employee by employee ID."
  tags:
    - hr
    - workforce
    - workday
capability:
  exposes:
    - type: mcp
      namespace: hr-ops
      port: 8080
      tools:
        - name: get-org-chart
          description: "Given a Workday employee ID, return manager name and direct reports list."
          inputParameters:
            - name: employee_id
              in: body
              type: string
              description: "Workday employee ID."
          call: workday.get-worker
          with:
            employee_id: "{{employee_id}}"
          outputParameters:
            - name: manager
              type: string
              mapping: "$.Worker.Manager.Name"
            - name: direct_reports
              type: array
              mapping: "$.Worker.DirectReports[*].Name"
  consumes:
    - type: http
      namespace: workday
      baseUri: "https://wd5-impl-services1.workday.com/ccx/service/elsevier"
      authentication:
        type: basic
        username: "$secrets.workday_user"
        password: "$secrets.workday_password"
      resources:
        - name: workers
          path: "/Human_Resources/v40.0/Get_Workers"
          operations:
            - name: get-worker
              method: GET

When an employee's role changes in Workday, updates their Salesforce user profile, adjusts GitHub team membership, and notifies the manager via Teams.

naftiko: "0.5"
info:
  label: "Workday Role Change Propagation"
  description: "When an employee's role changes in Workday, updates their Salesforce user profile, adjusts GitHub team membership, and notifies the manager via Teams."
  tags:
    - hr
    - identity
    - workday
    - salesforce
    - github
    - msteams
    - role-change
capability:
  exposes:
    - type: mcp
      namespace: hr-role-change
      port: 8080
      tools:
        - name: propagate-role-change
          description: "Given a Workday employee ID, old role, and new role, update Salesforce user permissions, adjust GitHub team membership, and notify the manager. Use on confirmed Workday position change events."
          inputParameters:
            - name: workday_employee_id
              in: body
              type: string
              description: "Workday worker ID of the employee changing roles."
            - name: new_role
              in: body
              type: string
              description: "New job title or role code as defined in Workday."
          steps:
            - name: get-worker
              type: call
              call: workday.get-worker
              with:
                worker_id: "{{workday_employee_id}}"
            - name: update-salesforce-user
              type: call
              call: salesforce.patch-user
              with:
                email: "{{get-worker.email}}"
                Title: "{{new_role}}"
                Department: "{{get-worker.department}}"
            - name: update-github-team
              type: call
              call: github.update-team-membership
              with:
                org: "elsevier"
                team_slug: "{{get-worker.department}}"
                username: "{{get-worker.github_username}}"
                role: "member"
            - name: notify-manager
              type: call
              call: msteams.send-message
              with:
                recipient_upn: "{{get-worker.manager_email}}"
                text: "Role change processed for {{get-worker.full_name}}: now {{new_role}}. Salesforce and GitHub updated."
  consumes:
    - type: http
      namespace: workday
      baseUri: "https://wd2-impl-services1.workday.com/ccx/api/v1"
      authentication:
        type: bearer
        token: "$secrets.workday_token"
      resources:
        - name: workers
          path: "/workers/{{worker_id}}"
          inputParameters:
            - name: worker_id
              in: path
          operations:
            - name: get-worker
              method: GET
    - type: http
      namespace: salesforce
      baseUri: "https://elsevier.my.salesforce.com/services/data/v58.0"
      authentication:
        type: bearer
        token: "$secrets.salesforce_token"
      resources:
        - name: users
          path: "/sobjects/User"
          operations:
            - name: patch-user
              method: PATCH
    - type: http
      namespace: github
      baseUri: "https://api.github.com"
      authentication:
        type: bearer
        token: "$secrets.github_token"
      resources:
        - name: team-members
          path: "/orgs/{{org}}/teams/{{team_slug}}/memberships/{{username}}"
          inputParameters:
            - name: org
              in: path
            - name: team_slug
              in: path
            - name: username
              in: path
          operations:
            - name: update-team-membership
              method: PUT
    - type: http
      namespace: msteams
      baseUri: "https://graph.microsoft.com/v1.0"
      authentication:
        type: bearer
        token: "$secrets.msteams_token"
      resources:
        - name: messages
          path: "/users/{{recipient_upn}}/sendMail"
          inputParameters:
            - name: recipient_upn
              in: path
          operations:
            - name: send-message
              method: POST

Retrieves a Zendesk author support ticket by ID, returning subject, status, and priority.

naftiko: "0.5"
info:
  label: "Zendesk Author Support Ticket Lookup"
  description: "Retrieves a Zendesk author support ticket by ID, returning subject, status, and priority."
  tags:
    - support
    - publishing
    - zendesk
capability:
  exposes:
    - type: mcp
      namespace: author-support
      port: 8080
      tools:
        - name: get-author-ticket
          description: "Given a Zendesk ticket ID, return subject, status, priority, and requester email."
          inputParameters:
            - name: ticket_id
              in: body
              type: string
              description: "Zendesk ticket ID."
          call: zendesk.get-ticket
          with:
            ticket_id: "{{ticket_id}}"
          outputParameters:
            - name: subject
              type: string
              mapping: "$.ticket.subject"
            - name: status
              type: string
              mapping: "$.ticket.status"
            - name: priority
              type: string
              mapping: "$.ticket.priority"
  consumes:
    - type: http
      namespace: zendesk
      baseUri: "https://elsevier.zendesk.com/api/v2"
      authentication:
        type: bearer
        token: "$secrets.zendesk_token"
      resources:
        - name: tickets
          path: "/tickets/{{ticket_id}}.json"
          inputParameters:
            - name: ticket_id
              in: path
          operations:
            - name: get-ticket
              method: GET

Fetches Zoom webinar registrations and syncs attendee data to HubSpot for follow-up marketing.

naftiko: "0.5"
info:
  label: "Zoom Webinar Registration Sync"
  description: "Fetches Zoom webinar registrations and syncs attendee data to HubSpot for follow-up marketing."
  tags:
    - marketing
    - events
    - zoom
    - hubspot
capability:
  exposes:
    - type: mcp
      namespace: events-ops
      port: 8080
      tools:
        - name: sync-webinar-registrants
          description: "Given a Zoom webinar ID, fetch registrants and sync to HubSpot contacts."
          inputParameters:
            - name: webinar_id
              in: body
              type: string
              description: "Zoom webinar ID."
          steps:
            - name: get-registrants
              type: call
              call: zoom.list-registrants
              with:
                webinar_id: "{{webinar_id}}"
            - name: sync-to-hubspot
              type: call
              call: hubspot.batch-create-contacts
              with:
                contacts: "{{get-registrants.registrants}}"
  consumes:
    - type: http
      namespace: zoom
      baseUri: "https://api.zoom.us/v2"
      authentication:
        type: bearer
        token: "$secrets.zoom_token"
      resources:
        - name: registrants
          path: "/webinars/{{webinar_id}}/registrants"
          inputParameters:
            - name: webinar_id
              in: path
          operations:
            - name: list-registrants
              method: GET
    - type: http
      namespace: hubspot
      baseUri: "https://api.hubapi.com"
      authentication:
        type: bearer
        token: "$secrets.hubspot_token"
      resources:
        - name: contacts
          path: "/crm/v3/objects/contacts/batch/create"
          operations:
            - name: batch-create-contacts
              method: POST