Service Desk MCP Server
Exposes your service desk agent as an MCP server, so clients file and track requests from inside Claude Desktop.
agentstreamemail
Source
/**
* Your service desk as an MCP server. Clients add one connector to
* Claude Desktop (or any MCP client) and can file requests and check
* status from inside their own AI chat. Every request lands in a stream
* you monitor, and the agent can only do what its tools allow.
*
* Setup:
* 1. Deploy. Swirls provisions the inbound OAuth application.
* 2. In Cloud, mint client credentials under the connector and send
* your client the connection details.
*/
secret vendor_keys {
vars: [OPENROUTER_API_KEY]
}
schema request_payload {
label: "Service request"
schema: @json {
{
"type": "object",
"required": ["summary", "urgency"],
"properties": {
"summary": { "type": "string", "description": "What the client needs, in one or two sentences." },
"urgency": { "type": "string", "enum": ["low", "normal", "high"], "description": "How urgent the request is." },
"contact_email": { "type": "string", "description": "Who to follow up with." }
}
}
}
}
workflow file_request {
label: "File Request"
description: "File a new service desk request and return its ticket id."
root {
type: code
label: "Create ticket"
inputSchema: request_payload
code: @ts {
const input = context.nodes.root.input
const ticketId = "REQ_" + Date.now().toString(36).toUpperCase()
return {
ticket_id: ticketId,
summary: input.summary,
urgency: input.urgency || "normal",
contact_email: input.contact_email || "",
filed_at: new Date().toISOString()
}
}
outputSchema: @json {
{
"type": "object",
"required": ["ticket_id", "summary", "urgency", "filed_at"],
"properties": {
"ticket_id": { "type": "string" },
"summary": { "type": "string" },
"urgency": { "type": "string" },
"contact_email": { "type": "string" },
"filed_at": { "type": "string" }
}
}
}
}
node notify_desk {
type: email
label: "Notify service desk"
from: @ts { return "[email protected]" }
to: @ts { return "[email protected]" }
subject: @ts {
const t = context.nodes.root.output
return "[" + t.urgency.toUpperCase() + "] " + t.ticket_id + ": " + t.summary
}
text: @ts {
const t = context.nodes.root.output
return "New service request " + t.ticket_id + "\n\nSummary: " + t.summary + "\nUrgency: " + t.urgency + "\nContact: " + (t.contact_email || "not provided") + "\nFiled: " + t.filed_at
}
}
node confirm {
type: code
label: "Confirm ticket"
code: @ts {
const t = context.nodes.root.output
return {
ticket_id: t.ticket_id,
status: "Filed with the service desk. Reference " + t.ticket_id + " to check progress."
}
}
schema: @json {
{
"type": "object",
"required": ["ticket_id", "status"],
"properties": {
"ticket_id": { "type": "string" },
"status": { "type": "string" }
}
}
}
}
flow {
root -> notify_desk
notify_desk -> confirm
}
}
workflow check_request {
label: "Check Request"
description: "Look up a service desk request by its ticket id."
root {
type: code
label: "Read ticket id"
inputSchema: @json {
{
"type": "object",
"required": ["ticket_id"],
"properties": {
"ticket_id": { "type": "string", "description": "The REQ_ ticket id to look up." }
}
}
}
code: @ts { return { ticket_id: context.nodes.root.input.ticket_id } }
outputSchema: @json {
{
"type": "object",
"required": ["ticket_id"],
"properties": { "ticket_id": { "type": "string" } }
}
}
}
node find_ticket {
type: stream
label: "Find ticket"
stream: service_requests
version: v1
filter: @ts {
return { ticket_id: { eq: context.nodes.root.output.ticket_id } }
}
}
node answer {
type: code
label: "Answer"
code: @ts {
const rows = context.nodes.find_ticket.output || []
if (!rows.length) {
return { found: false, status: "No request found with that ticket id." }
}
const t = rows[0]
return {
found: true,
status: "Request " + t.ticket_id + " (" + t.urgency + ") was filed at " + t.filed_at + " and is with the service desk: " + t.summary
}
}
schema: @json {
{
"type": "object",
"required": ["found", "status"],
"properties": {
"found": { "type": "boolean" },
"status": { "type": "string" }
}
}
}
}
flow {
root -> find_ticket
find_ticket -> answer
}
}
stream service_requests {
label: "Service requests"
workflow: file_request
version: v1
versions: {
v1 {
schema: @json {
{
"type": "object",
"properties": {
"ticket_id": { "type": "string" },
"summary": { "type": "string" },
"urgency": { "type": "string" },
"contact_email": { "type": "string" },
"filed_at": { "type": "string" }
}
}
}
prepare: @ts {
return context.output.root
}
}
}
}
agent service_desk {
label: "Service Desk"
description: "Files and tracks service desk requests for clients."
provider: openrouter
model: "google/gemini-2.5-flash"
secrets: vendor_keys
maxSteps: 8
tools: [file_request, check_request]
system: @ts {
return [
"You are the service desk intake agent for a managed service provider.",
"When a client describes a problem, file it with the file_request tool and give them the ticket id.",
"When they ask about an existing request, look it up with check_request.",
"Set urgency to high only for outages or security issues. Be brief and concrete.",
].join("\n")
}
}
connector client_desk {
label: "Service desk connector"
description: "Lets a client's MCP-enabled AI reach the service desk agent."
}
channel desk_mcp {
label: "Service Desk (MCP)"
platform: mcp
agent: service_desk
connector: client_desk
}
Flow
Trigger to workflow
Add a trigger block to your .swirls file to start work from an event.
Workflow nodes