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
CODE
Code Node
Create ticket
EMAIL
Email Node
Notify service desk
CODE
Code Node
Confirm ticket
Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.