mcp-server-rt
OfficialThis server connects an AI assistant to a Request Tracker (RT) instance via MCP, enabling natural-language ticket management and queue administration.
Search tickets with TicketSQL queries
View ticket details, transaction history, and full message content
Create tickets with metadata, custom fields, links, and attachments
Update tickets, add internal comments, and send replies to requestors
Retrieve and save ticket attachments
Explore queues, custom field definitions, users, and the current user
Fetch a TicketSQL grammar reference for complex queries
Administer queues: create/update queues, manage watchers, groups, custom fields, lifecycles, and rights
Use a guided
create-queueprompt to design and build a queue configuration step by step
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@mcp-server-rt Show me open tickets in the Support queue."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
mcp-server-rt
An MCP (Model Context Protocol) server that connects AI assistants to a live RT (Request Tracker) instance. Search tickets, view history, create and update tickets — all from a natural language conversation.
Features
Search tickets using RT's full TicketSQL query language
Read ticket details including full transaction history
Create tickets setting initial content and all ticket metadata: status, owner, requestors, due dates, custom fields, custom roles, and links
Update tickets reply, comment, and update tickets, with the same full field support
Queue and user discovery — list queues, inspect custom field definitions, look up users by name or email
TicketSQL grammar reference — the AI can consult the full RT 6.0.3 syntax guide before constructing complex queries
Queue administration — build and configure queues: lifecycles, user-defined groups and their members, custom fields, rights, and queue watchers
Guided queue creation — a
create-queueprompt that interviews you about the workflow, recommends a configuration, confirms the plan, then builds it
Related MCP server: rt-mcp
Requirements
RT 6.0 or later with REST 2.0 API enabled (included by default). The lifecycle and rights tools need RT 6.0.3 or later, which is when RT added those REST 2.0 endpoints.
Node.js 18 or later
An RT authentication token
Installation
npm install -g mcp-server-rtOr use without installing via npx mcp-server-rt.
Creating an RT Auth Token
In RT: Logged in as → Settings → Auth Tokens → Create
Give the token a name (e.g. "Claude") and copy the generated token string.
The token is associated with the user account, so all operations in RT from Claude using that token will be logged as performed by that user. So everything you do via Claude still gets logged in RT as you, including emails sent on comments and replies.
Users need to be granted the right ManageAuthTokens to see the Auth Tokens menu.
AI Client Setup
MCP is an open standard — this server works with any MCP-compatible AI client. Configuration varies by client.
Claude Desktop ✓ (tested)
Install the .mcpb extension package from the releases page. In the Claude app, go to Customize → Connectors, find RT, and enter your RT URL and auth token.
Alternatively, add manually to ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):
{
"mcpServers": {
"rt": {
"type": "stdio",
"command": "npx",
"args": ["mcp-server-rt"],
"env": {
"RT_URL": "https://rt.example.com",
"RT_TOKEN": "your-auth-token"
}
}
}
}Claude Code ✓ (tested)
Add to .mcp.json in your project root:
{
"mcpServers": {
"rt": {
"type": "stdio",
"command": "npx",
"args": ["mcp-server-rt"],
"env": {
"RT_URL": "https://rt.example.com",
"RT_TOKEN": "your-auth-token"
}
}
}
}Other MCP-compatible clients
Any client that supports MCP stdio servers should work. Consult your client's documentation for how to register a stdio MCP server with environment variables. The server entry point is mcp-server-rt (or node /path/to/dist/index.js for a local build).
Tools
Tickets, queues, and users
Tool | Description |
| Search tickets using RT's TicketSQL query language |
| Get details for a specific ticket by ID |
| Get transaction history (comments, replies, status changes) |
| Get full details of a single transaction, including decoded message content |
| Get details about a queue by ID or name |
| List all available queues |
| Get custom field definitions and lifecycle name for a queue |
| Search for RT users by name or email |
| Get the RT user associated with the configured auth token |
| Fetch the full TicketSQL grammar reference (for complex queries) |
| Create a new ticket |
| Update ticket fields (status, owner, priority, dates, watchers, links, custom fields) |
| Add an internal comment (not visible to the requestor), optionally setting custom fields |
| Send a reply to the requestor, optionally setting custom fields |
| List all attachments on a ticket |
| Retrieve a single attachment by ID |
| Save an attachment to a local file |
Queue administration
Building and configuring queues. The lifecycle and rights tools need RT 6.0.3 or later.
Tool | Description |
| Create a new queue |
| Update an existing queue's settings |
| Set the members of a queue role (Cc, AdminCc, or a custom role) |
| List user-defined groups with names and descriptions |
| Get details about a group by ID or name |
| Create a new user-defined group |
| List a group's members (RT returns IDs and types only) |
| Add users to a group, by user ID |
| Remove a user from a group |
| Create a custom field |
| Search existing custom fields before creating a duplicate |
| Apply a custom field to a queue |
| Add a selectable value to a custom field |
| List the objects a custom field is applied to |
| Stop applying a custom field to an object |
| List lifecycles with their statuses and transitions |
| Get one lifecycle's full definition |
| Create a lifecycle, optionally cloning an existing one |
| Replace a lifecycle's configuration |
| Map this lifecycle's statuses onto other lifecycles |
| Check a lifecycle definition without saving it |
| Delete a lifecycle no queue or catalog uses |
| List the rights that can be granted on an object |
| List the rights currently granted on an object |
| Grant rights to a user, group, or role |
| Revoke a single right |
Prompts
Prompt | Description |
| Interactive consultant that discovers a team's workflow, recommends a queue configuration, confirms the plan, then builds it — queue, lifecycle, groups, rights, custom fields, and watchers |
Usage Examples
Example 1: Finding and triaging unowned tickets
User: "Show me active tickets in the Support queue with no owner."
Claude calls: search_tickets with query Queue = 'Support' AND Status = '__Active__' AND Owner = 'Nobody'. No fields parameter is needed — the server sends a default set that identifies each ticket, with queue and owner as names rather than ID stubs.
Result: A table of unowned active tickets with subject, requestor, and when each was last updated, ready to assign or act on.
Example 2: Reading recent correspondence on a ticket
User: "Show me the most recent reply on ticket 1234."
Claude calls: get_ticket_history to get the list of transactions, identifies the most recent Correspond entry, then calls get_transaction to fetch and decode the full message content.
Result: The decoded text of the reply, including who sent it and when.
Example 3: Creating a fully configured ticket
User: "Create a ticket in the Projects queue titled 'Update onboarding docs', assign it to alice, set the due date to next Friday, and link it to ticket 500."
Claude calls: create_ticket with Queue, Subject, Owner, Due, and RefersTo all set in a single API call.
Result: New ticket created with all fields set. Claude confirms the ticket number and a summary of what was set.
Example 4: Updating ticket status with a reply
User: "Resolve ticket 789 and let the requestor know we've pushed a fix in version 6.0.3."
Claude calls: add_reply with the message content and Status: 'resolved' to close the ticket and notify the requestor in one step.
Result: Ticket resolved, requestor notified. Claude confirms both actions completed.
Example 5: Querying with custom fields
User: "Find all open tickets in the General queue where the Category field is set to 'Bug'."
Claude calls: get_queue_fields to confirm the exact custom field name, then search_tickets with Queue = 'General' AND Status = '__Active__' AND CF.{Category} = 'Bug'.
Result: A list of matching bug tickets with subject, owner, and creation date.
How It Works
This server implements the Model Context Protocol over stdio. The AI client translates natural language requests into TicketSQL queries or RT API calls, invokes the appropriate tool, and presents the results. The server itself is a thin proxy — it passes queries directly to RT's REST 2.0 API and returns the JSON response.
For complex searches, the AI can call get_ticketsql_grammar to consult the full TicketSQL syntax reference before constructing a query.
Configuration Reference
Environment Variable | Description |
| Base URL of your RT instance (e.g. |
| RT authentication token |
Development
npm install
npm run build # compile TypeScript to dist/
npm test # run tests
npm run dev # watch modeRun locally against your RT instance:
RT_URL=https://rt.example.com RT_TOKEN=your-token node dist/index.jsCompatibility
RT 6.0+ (REST 2.0 API)
Node.js 18+
Privacy
This server does not collect, store, or transmit any data to Best Practical or any third party. All communication is directly between your AI client and your own RT instance using the URL and credentials you provide. No usage data, ticket content, or credentials are sent anywhere other than your configured RT server.
See the Best Practical Privacy Policy for general information about our privacy practices.
Support
For questions and discussion, visit the Best Practical Community Forum.
To report a bug, create a ticket on our public RT instance. Note that this is a public RT instance, so the information you share will be visible to others.
Is RT mission critical for you? Commercial support for RT and this connector is available from Best Practical. Contact us at sales@bestpractical.com.
License
GPL-2.0
Available Tools
43 toolsadd_commentADestructive
Add an internal comment to a ticket (not visible to the requestor)
Inputs: id (required), Content, TimeTaken, Attachments, +2 more.
add_custom_field_valueADestructive
Add one or more selectable values to a custom field (for Select, Autocomplete, etc. types). Pass a single value with Name, or multiple values with the Values array.
Inputs: id (required), Name, Values, Category, +2 more.
add_group_membersADestructive
Add one or more users to a group. Members are specified by user ID. Use lookup_user to find user IDs by name or email. Adding a user who is already a member has no effect.
Inputs: id (required), members (required).
add_replyADestructive
Send a reply (correspondence) on a ticket, visible to the requestor
Inputs: id (required), Status, Content, TimeTaken, +3 more.
apply_custom_fieldADestructive
Apply a custom field to a specific object or globally. The CF's LookupType determines what kind of object it can apply to (e.g. a ticket CF applies to queues). Use ObjectId 0 to apply globally. Note: applying globally removes all specific object applications.
Inputs: id (required), ObjectId (required).
create_custom_fieldADestructive
Create a new custom field. After creating, use apply_custom_field to apply it to specific queues or globally. Use add_custom_field_value to add values to Select-type fields.
Inputs: Name (required), Type (required), LookupType (required), Pattern, +3 more.
create_groupADestructive
Create a new user-defined group. After creating, use add_group_members to add users and grant_rights to give the group permissions on queues.
Inputs: Name (required), Description.
create_lifecycleADestructive
Create a new lifecycle. Optionally clone an existing one as a starting point. A lifecycle defines the statuses and transitions for tickets in queues that use it. After creating, use update_lifecycle to customize statuses and transitions.
Inputs: name (required), type, clone.
create_queueADestructive
Create a new RT queue. Returns the new queue ID and URL. After creating, use manage_queue_watchers to set up Cc/AdminCc members, grant_rights to configure permissions, and create_custom_field + apply_custom_field to add custom fields.
Inputs: Name (required), Lifecycle, Description, SLADisabled, +2 more.
create_ticketADestructive
Create a new ticket in RT
Inputs: Queue (required), Subject (required), Cc, Due, +21 more.
delete_lifecycleADestructive
Delete a lifecycle. Fails if any queue or catalog still uses it — reassign those to another lifecycle first (get_lifecycle reports them under used_by). Useful for cleaning up a lifecycle created by mistake.
Inputs: name (required).
get_attachmentARead-only
Retrieve a single attachment by ID. Text content is returned decoded; binary content is returned as MIME Base64. Use get_ticket_attachments or get_transaction to find attachment IDs.
Inputs: id (required).
get_available_rightsARead-only
Get the rights that can be granted on a queue, custom field, group, class, catalog, or globally. Returns rights grouped by category, and which categories come back depends on the object: RT's own are General, Staff and Admin, and a group offers only some of them. A queue — and the global object — also carries a Status category, holding one right for each status transition a lifecycle reserves behind a named right, so that is where a custom sign-off right appears once a lifecycle defines it. Read the categories from the response rather than assuming a fixed set.
Inputs: object_type (required), object_id.
get_current_userARead-only
Get the RT user account associated with the configured auth token. Use this to determine who "I" or "me" refers to when the user asks to assign tickets to themselves, find their own tickets, etc.
Inputs: no named parameters.
get_groupARead-only
Get details about a specific group by numeric ID
Inputs: id (required).
get_lifecycleARead-only
Get a lifecycle's full configuration including statuses (initial, active, inactive), allowed transitions, default statuses, and which queues/catalogs use it.
Inputs: name (required).
get_queueARead-only
Get details about a specific queue by ID or name
Inputs: id (required).
get_queue_fieldsARead-only
Get custom fields (with types and allowed values) and lifecycle name for a queue. Returns three separate groups, because RT applies custom fields to three different things: CustomFields are set on tickets in the queue (this is what you want when creating or updating a ticket), QueueCustomFields are set on the queue itself and include the queue's CurrentValues (RTIR uses these for RTIR Constituency and RTIR default WHOIS server), and TransactionCustomFields are set on individual comments and replies. When the user asks what custom fields a queue has, report all three groups and say which is which. Each field carries a ContentFormat saying how its value is rendered: "html" (send markup; a bare newline shows nothing), "plain-text-multiline" (send plain text; newlines become line breaks), "plain-text" (shown exactly as typed), "wikitext" (wiki markup), "file" (an uploaded image or attachment rather than text), "date", or "datetime" (send local time; RT reads it in the user's timezone). Check it before writing a multi-line or formatted value. Every applied field is always listed. If RT permits seeing that a field is applied but not reading the field itself, the entry carries id, Name and a DetailsUnavailable message explaining why; a QueueCustomFields entry still carries its CurrentValues too. The field is still applied to the queue either way, and a field in CustomFields can still be set on a ticket. Tell the user which fields came back without details rather than reporting them as missing.
Inputs: id (required).
get_ticketARead-only
Get detailed information about a specific ticket by its ID
Inputs: id (required), fields, subfields.
get_ticket_attachmentsARead-only
List all attachments on a ticket (names, MIME types, sizes, IDs)
Inputs: id (required), page, fields, per_page.
get_ticket_historyARead-only
Get the transaction history for a ticket. Returns a list of transactions (comments, replies, status changes, etc.). Most entries name the field changed in Field and carry its old and new values. Two kinds do not. An owner or watcher change (Type SetWatcher, AddWatcher or DelWatcher) puts a numeric user ID in OldValue and NewValue, and no tool here turns one into a name — describe the change without inventing one. A custom field change (Type CustomField) puts the field's numeric ID in Field rather than its name, which get_queue_fields maps back; its OldValue and NewValue hold the values as usual, while OldReference and NewReference are row IDs that nothing here resolves.
Inputs: id (required), page, fields, per_page.
get_ticketsql_grammarARead-only
Returns the TicketSQL grammar reference for RT 6.0.3. Consult this before writing any TicketSQL query — especially for Status conditions, date/time fields, custom fields, and link fields where syntax is non-obvious.
Inputs: no named parameters.
get_transactionARead-only
Get the full details of a single transaction including decoded message content. Use this after get_ticket_history to read the actual text of a reply or comment.
Inputs: id (required).
grant_rightsADestructive
Grant rights on a queue, custom field, group, class, catalog, or globally. Specify a single right with Right + (User or Group), or pass an array of grants for bulk operation. Returns 409 if a right is already granted.
Inputs: object_type (required), User, Group, Right, +2 more.
list_custom_field_applicationsARead-only
List which objects a custom field is currently applied to, including whether it is applied globally.
Inputs: id (required), page, per_page.
list_group_membersARead-only
List the members of a group. RT returns only an id and a type (user or group) per member — this collection ignores a fields parameter, so names cannot be requested. get_group returns the same membership already resolved: a user member carries its username as its id, a group member its numeric id. Use get_group when the user needs members named rather than listed as bare ids.
Inputs: id (required), page, users, groups, +2 more.
list_groupsARead-only
List user-defined groups. Returns group names, descriptions, and IDs. Use this to check for existing groups before creating new ones.
Inputs: fields.
list_lifecyclesARead-only
List all available lifecycles. Each lifecycle defines the statuses and transitions for tickets in queues that use it.
Inputs: type.
list_queuesBRead-only
List all available queues
Inputs: fields.
list_rightsARead-only
List rights currently granted on a queue, custom field, group, class, catalog, or globally. Can filter by user or group.
Inputs: object_type (required), page, user, group, +2 more.
lookup_userBRead-only
Search for RT users by name or email address
Inputs: query (required), page, fields, per_page.
manage_queue_watchersADestructive
Set the members of a queue role (Cc, AdminCc, or a multi-value custom role). Pass the complete member list — it replaces existing members. Members can be usernames, email addresses, or user/group IDs. To add a group by name, prefix it with "group:" (e.g. "group:Facilities Managers"); a bare name is looked up as a user and fails. Only user-defined groups resolve this way — not system groups like Everyone, and not role groups. Single-value custom roles (like Owner) cannot have queue-level members. A member that cannot be resolved is skipped and reported in the returned messages while the call still succeeds, so confirm every member you passed was actually added — see PARTIAL UPDATES.
Inputs: id (required), Cc, AdminCc, CustomRoles.
remove_custom_field_applicationADestructive
Remove a custom field from a specific object or remove its global application. Use ObjectId 0 to remove the global application.
Inputs: id (required), ObjectId (required).
remove_group_memberBDestructive
Remove a user from a group
Inputs: id (required), member_id (required).
revoke_rightADestructive
Revoke a right from a user or group on a queue, custom field, group, class, catalog, or globally. The principal goes in the URL path here rather than a request body, and RT resolves a name there only for a user: a group has to be given as its numeric ID, and a group name answers 404 even though grant_rights resolves that same name. RT answers 404 for a right that was never granted too, so a group name passed here looks like nothing to revoke rather than a bad parameter. Use get_group or list_groups to get the ID.
Inputs: Right (required), object_type (required), User, Group, +1 more.
save_attachmentARead-only
Save an attachment to a local file. The MCP server writes the file directly, so this works on any platform. If path is a directory, the original filename is used.
Inputs: id (required), path (required).
search_custom_fieldsARead-only
Search for existing custom fields. Use this before creating new ones to avoid duplicates. Search by Name, Type, LookupType, or any combination. Returns matching custom fields with their IDs, types, and descriptions.
Inputs: Name, Type, page, fields, +2 more.
search_ticketsARead-only
Search for tickets using RT's TicketSQL query language. TicketSQL has non-obvious syntax — consult get_ticketsql_grammar before writing any query involving Status, date conditions, custom fields, or special values. Key syntax notes: Status has meta-values Active and Inactive that match all active/inactive statuses across lifecycles (e.g. Status = 'Active' rather than Status = 'open'). Basic examples: "Queue = 'General' AND Owner = 'Nobody'", "Subject LIKE 'login'". A useful default field set is sent automatically, with Queue and Owner expanded to names, so pass fields or subfields only when the context calls for a different set.
Inputs: query (required), page, order, fields, +3 more.
update_lifecycleADestructive
Update a lifecycle's configuration. Pass the full lifecycle definition including initial, active, inactive status arrays, transitions, rights, actions, and defaults. This REPLACES the stored configuration — any key you omit is dropped, including keys inherited from a create_lifecycle clone. RT fills in a missing defaults.on_create from the first initial status, and falls back to ModifyTicket — DeleteTicket for deleted — where rights are missing, but it does that in memory as it loads the config: neither key reappears in get_lifecycle, so one absent there is a working default rather than damage to repair. Omitted actions, colors, status_metadata and transition_metadata are simply lost. Cloning "default" inherits a full set of metadata, so omitting those two keys here silently strips the descriptions from every status. Use get_lifecycle first to get the current config, then modify and send the whole thing back. The lifecycle is validated before saving; any warning fails the update with a 400.
Inputs: name (required), active, colors, rights, +7 more.
update_lifecycle_mapsADestructive
Update the status mappings between this lifecycle and other lifecycles. Maps define how statuses translate when tickets move between queues with different lifecycles. Format: {"lifecycle_a -> lifecycle_b": {"status_in_a": "status_in_b", ...}}
Inputs: maps (required), name (required).
update_queueADestructive
Update an existing queue's settings (name, description, lifecycle, email addresses, etc.). To manage watchers (Cc, AdminCc), use manage_queue_watchers instead. Each field is applied independently and the call succeeds even when some of them fail, so check the returned messages for every change you asked for — see PARTIAL UPDATES.
Inputs: id (required), Name, Disabled, Lifecycle, +4 more.
update_ticketADestructive
Update an existing ticket. Pass each property to change as a top-level parameter (e.g. Due, Status, Owner) — do NOT use a nested "fields" object. Links are changed only with the Add/Delete fields below: a bare relation name such as RefersTo, Parent or Children is refused, because RT would treat it as the complete list for that relation and silently remove every other link of the type.
Inputs: id (required), Cc, Due, Told, +25 more.
validate_lifecycleARead-only
Check a lifecycle definition without saving it. Takes the same payload as update_lifecycle and reports whether RT would accept it, with a warning for each problem found (unknown statuses, malformed transitions or actions, and so on). Use this to dry-run a custom lifecycle before writing it, since update_lifecycle rejects the whole payload if anything is wrong. RT checks only the payload sent: it never reads the lifecycle the name refers to, which is why the name need not exist yet and why it does nothing but label the warnings. So send a complete definition rather than the part being changed — transitions on their own report every status in them as nonexistent, including statuses the named lifecycle already has. Read the current definition with get_lifecycle, change it, and validate the whole thing.
Inputs: name (required), active, colors, rights, +7 more.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 43 tools
Each tool targets a distinct resource and action, and close pairs like list_group_members vs get_group and get_ticket_history vs get_transaction are explicitly differentiated in their descriptions. Even with 43 tools, the annotations make the intended selection clear without true overlap.
The vast majority of tools follow a clear verb_noun pattern such as get_, list_, create_, update_, add_, and remove_. Minor singular/plural inconsistencies like grant_rights vs revoke_right and add_group_members vs remove_group_member prevent a perfect score.
43 tools is a very large surface for a single server, spanning tickets, queues, groups, lifecycles, rights, and custom fields. Even though each tool appears purposeful, the count is well beyond the typical well-scoped MCP toolset and creates significant selection burden for agents.
Ticket, queue, lifecycle, and rights workflows are covered thoroughly, including create/read/update operations. However, group and custom field management lack update/delete operations, and user management is limited to lookup and current-user, leaving notable gaps in the administrative lifecycle.
Maintenance
Related MCP Connectors
Connect AI clients to your Dactic work context through a hosted MCP service.
Remote MCP server for managing Muninx tickets, messages, ticket search, and support analytics.
Connect any AI agent to 1,000+ apps and 27,000+ actions through one remote MCP server (OAuth).
Hosted MCP server connecting AI assistants to 9,000+ apps and 40,000+ actions via Zapier.
Related MCP Servers
AlicenseBqualityBmaintenanceAn MCP server that exposes the Tickiti helpdesk API to AI assistants, enabling ticket management and helpdesk operations via natural language.11MIT- AlicenseNot gradedqualityDmaintenanceMCP server for Request Tracker REST2 API, enabling ticket, queue, user, and asset management via natural language.MIT
- AlicenseAqualityCmaintenanceMCP server that exposes the i-net HelpDesk Ticket Web-API as tools for AI agents, enabling ticket search, reading, creation, and actions like replying, closing, and escalating, with attachment support.8MIT
- AlicenseBqualityBmaintenanceEnables secure interaction with Request Tracker (RT) for ticket management, including search, creation, updates, resolution, time tracking, and workflow automation.17AGPL 3.0