SAS MCP Server
OfficialServer Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| VIYA_ENDPOINT | Yes | The URL of your SAS Viya server |
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
This server publishes no instructions, or was last inspected before Glama recorded them.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tools | {
"listChanged": true
} |
| logging | {} |
| prompts | {
"listChanged": false
} |
| resources | {
"subscribe": false,
"listChanged": false
} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| execute_sas_codeA | Executes the provided SAS code in the Viya environment and returns information about the completed Job. This will create a job definition for the SAS code, execute it, and then retrieve the results. IMPORTANT — state persists between calls: the code runs in a reusable
compute session that is kept warm and shared across calls (per user),
so SAS state — WORK tables, macro variables, and assigned librefs —
survives between successive Tip: to reach CAS data, prefer |
| list_compute_contextsB | List available compute contexts on the Viya environment. |
| reset_compute_sessionA | Reset (delete) the cached compute session for a compute context. The server keeps one reusable SAS compute session per user and compute
context so repeat calls skip the slow session spin-up; SAS state (WORK
tables, macro variables, assigned librefs) therefore persists across
|
| catalog_searchA | Search the SAS Information Catalog for assets (tables, columns, reports, ...). The catalog is a metadata index across the whole Viya environment, so this
finds assets without needing to know their server/library first. Each hit
includes the asset's The
|
| catalog_search_helperA | Discover how to search the catalog: list facets, or values for one facet. Call with no |
| catalog_find_instanceA | Resolve the catalog instance for a source-asset URI.
|
| catalog_list_agentsA | List SAS Information Catalog discovery agents. Agents crawl a data source (server/library) to discover assets and collect
their metadata into the catalog. Use |
| catalog_run_agentA | Start a catalog discovery agent run (asynchronous). Triggers the agent to crawl its data source and populate/refresh catalog
metadata. The run is asynchronous — results are applied to the catalog in
the background; poll |
| catalog_get_agent_historyA | Get the execution history of a catalog agent's runs. Each record reports a run's status and how much metadata it populated
(tables enumerated/added/updated/removed), so you can confirm a run started
by |
| catalog_run_adhoc_analysisA | Submit an ad-hoc analysis (profiling) job for a table in the catalog. Profiles the table — computing the data dictionary, column statistics, and
data-quality metrics that The three NLP job parameters are enabled by default — they drive the
semantic enrichment that populates an asset's |
| catalog_get_adhoc_analysisA | Get the status of an ad-hoc analysis job, and whether its profile is ready. The job reaching a terminal |
| catalog_download_table_profileA | Download a catalog table's data dictionary and profile as CSV. Returns the table's column metadata plus, by default, its profile (column
statistics and data-quality metrics). If the table has not been profiled yet,
this returns a recommendation to run Identify the table by either |
| list_compute_librariesA | List the SAS libraries (librefs) assigned in a compute context. Runs in the reusable per-user compute session for the context, so it
also sees libraries created by prior |
| list_compute_tablesA | List the tables in a SAS library within a compute context. These are SAS/Compute tables (e.g. WORK or an assigned libref), distinct from in-memory CAS tables (see list_castables). Runs in the reusable per-user compute session for the context. |
| list_compute_columnsA | List the columns of a table in a SAS library within a compute context. Runs in the reusable per-user compute session for the context. |
| list_cas_serversA | List available CAS servers on the Viya environment. |
| list_caslibsA | List CAS libraries (caslibs) available on a CAS server. |
| list_castablesC | List tables in a CAS library. |
| list_source_tablesA | List source tables that are NOT yet loaded into memory in a CAS library. These are the candidates for |
| get_castable_infoA | Get metadata for a CAS table (row count, column count, size, etc.). |
| get_castable_columnsA | Get column metadata for a CAS table (names, types, labels, formats). A missing table returns a structured |
| get_castable_dataB | Fetch rows from a CAS table with column names. |
| get_compute_table_dataA | Fetch rows from a table in a SAS library, with column names. The compute-tier counterpart of Runs in the reusable per-user compute session for the context, so
WORK tables from earlier |
| query_dataA | Run a FedSQL SELECT against CAS or compute data and return the rows. One SQL surface over both storage tiers, so exploring a caslib table and a SAS library table use the same tool and the same dialect. The query runs in the reusable compute session; nothing is persisted — the result is materialised into session scratch, read back, and dropped. Pick the tier with
Dialect notes (FedSQL, not PROC SQL): joins (inner/left/right/full/
cross), subqueries, UNION, GROUP BY/HAVING/ORDER BY, and scalar functions
work. There is no WITH/CTE — use a derived table Row capping is done by this tool, not by your SQL: any LIMIT you write is
ignored in favour of |
| upload_dataA | Upload a data file into a CAS table — read by the server, not the model. Provide the data by reference through exactly one of:
Either way the bytes are read server-side and never pass through the calling
model's context window. Sources larger than The casManagement uploadTable endpoint only accepts an uploaded file (multipart
form-data) and has no URL parameter, so Formats. Per the uploadTable API: csv, xls, xlsx (single sheet), sas7bdat,
sashdat; |
| upload_inline_dataA | Create a small CAS table from inline delimited text passed as a string. Use this only for tiny, hand-built tables — a lookup/mapping table the model
constructs on the fly, or a quick test table — because the whole payload travels
through the model's context as a tool argument. For anything larger, or any file
you already have, use Text formats only: |
| promote_table_to_memoryA | Load a source table into CAS memory at global scope (visible to all sessions). Loads the table from its caslib data source and promotes it to global
scope via the casManagement |
| list_filesA | List files in the Viya Files Service. |
| upload_fileA | Upload a file to the Viya Files Service, optionally into a Content folder. Provide the file content through exactly one of:
|
| download_fileA | Download file content from the Viya Files Service. |
| list_reportsB | List Visual Analytics reports. |
| get_reportA | Get a Visual Analytics report's metadata and definition. |
| export_reportA | Export a Visual Analytics report (or specific report objects) in any format the VA service exposes, via its synchronous export endpoints. Formats (
|
| describe_report_objectsA | Discover what a Visual Analytics report can contain — operations and objects. Call this to learn how to build a report before calling
|
| create_reportA | Create a Visual Analytics report and return its id for further edits. Creates an empty report shell, or — if you pass |
| apply_report_operationsA | Apply an ordered batch of operations to a report — the authoring workhorse. This is how you add pages, add objects (any of the ~60 VA visual, control,
and content types), set parameters, and swap data sources. Operation keys (one per array element): Layout & titles (see
The tool validates every operation against the object catalog before any HTTP call (unknown/typo'd object type, non-addable object, bad data-role names or arity, disallowed object/placement keys) and reports ALL invalid operations at once. It also handles the ETag optimistic-concurrency handshake for you, retrying once transparently on a concurrent edit. |
| get_report_outlineA | Read a report's structure: pages → objects with the handles other tools need. Reduces the stored report definition to a compact outline —
per page its internal name and label, per object its name (
Returns |
| copy_reportA | Copy a Visual Analytics report to a new report, returning the copy's id. Useful for tailoring a report to a new audience or for the copy-and-replace
pattern — copy, then |
| delete_reportA | Delete a Visual Analytics report and its content. There is no per-object undo in the report API, so deleting and rebuilding
(or copying first) is how you discard an unwanted report. Returns
|
| submit_batch_jobA | Submit a SAS job for asynchronous execution via the Job Execution service. |
| get_job_statusA | Check the status of a submitted job. |
| list_jobsA | List recent jobs from the Job Execution service. |
| cancel_jobA | Cancel a running job. |
| get_job_logA | Retrieve the log of a completed job. |
| list_ml_projectsB | List AutoML pipeline automation projects. |
| create_ml_projectA | Create a new AutoML pipeline automation project from a CAS table. The training table must already be loaded into CAS memory at global
scope. This tool verifies that first and returns an actionable error
otherwise (use |
| register_ml_champion_modelB | Register the champion model from an AutoML pipeline automation project to the Model Repository. |
| publish_ml_champion_modelB | Publish the champion model from an AutoML pipeline automation project to the Model Repository. |
| run_ml_projectC | Run an AutoML pipeline automation project. |
| list_registered_modelsB | List models in the Model Repository. |
| list_publishing_destinationsB | List available publishing destinations. |
| list_mas_modulesA | List published scoring models and decisions (MAS modules). |
| get_mas_module_step_signatureA | Fetch a MAS module step's input/output variable signature. Call before |
| score_dataB | Score data against a published model or decision (MAS module). |
| create_business_rulesetA | Create a new SAS Business Rules rule set. A rule set with no rules cannot be used in a decision flow — follow up
with |
| update_business_rulesetA | Update an existing SAS Business Rules rule set's name/description/signature. Changing the signature can invalidate existing rules that reference
removed variables — check with |
| get_business_rulesetA | Fetch a single SAS Business Rules rule set by ID. |
| list_business_rulesetsA | List SAS Business Rules rule sets, optionally filtered by name substring. |
| delete_business_rulesetA | Permanently delete a SAS Business Rules rule set. Only call this once the rule set is confirmed unused by any decision flow — deleting a rule set still referenced by a decision fails. |
| lock_business_ruleset_revisionA | Lock the current state of a rule set as an immutable revision. Decision steps reference a specific rule set revision (versionId), not the live working copy, so a revision must exist before wiring a rule set into a decision flow — call again after editing rules if a decision needs to pick up the changes. The revision-creation request replaces the rule set's full content
from the body sent, so this fetches the rule set with its rules
included ( |
| list_business_ruleset_revisionsA | List all locked revisions of a rule set. |
| create_business_ruleA | Create a new rule inside an existing SAS Business Rules rule set. A rule set can hold multiple rules, each evaluated per its conditional
type. Condition/action expressions must include the variable name
directly (e.g. |
| update_business_ruleA | Update an existing rule inside a SAS Business Rules rule set. |
| get_business_ruleA | Fetch a single rule's definition from a SAS Business Rules rule set. |
| list_business_rulesA | List all rules inside a SAS Business Rules rule set. |
| delete_business_ruleA | Permanently delete a rule from a SAS Business Rules rule set. |
| create_decision_flowB | Create a new SAS Intelligent Decisioning flow chaining rule set steps. |
| update_decision_flowA | Update an existing SAS Intelligent Decisioning flow. Pass ALL rule set steps (existing + new) — the full flow is replaced on update, it is not a partial patch. |
| get_decision_flowA | Fetch the current state of a SAS Intelligent Decisioning flow. |
| list_decision_flowsA | List SAS Intelligent Decisioning flows, optionally filtered by name substring. |
| delete_decision_flowA | Permanently delete a SAS Intelligent Decisioning flow. |
| get_decision_flow_codeA | Retrieve the generated DS2 execution code for a decision flow. |
| lock_decision_flow_revisionA | Lock the current state of a decision flow as an immutable revision. Call after a successful create/update to freeze the approved state as
a point-in-time snapshot referenceable by |
| list_decision_flow_revisionsA | List all locked revisions of a decision flow. |
| get_decision_flow_revisionA | Fetch the content of a specific locked decision revision. |
| publish_decision_flowA | Publish a locked decision revision to a Micro Analytic Score (MAS) destination. Required before Publishing is asynchronous and the resulting MAS module ID is
server-generated — it is NOT |
| list_glossary_term_typesA | List the term types defined in the SAS Business Glossary. A term type is the template a term is created from: it fixes which
custom attributes the term carries and which of them are mandatory. Every
term belongs to exactly one, and the choice is immutable after creation —
so pick the type before calling
|
| get_glossary_term_typeA | Get a term type and the attribute contract its terms must satisfy. Call this before creating or updating a term: it names every custom
attribute, its data type, whether it is required, the exact values a
single- or multi-select accepts, and |
| create_glossary_term_typeA | Create a term type — the template that fixes what a term must carry. A term type declares the custom attributes every term of that type holds, and which are mandatory. Without this tool a deployment's types can only be created in the SAS UI, so a glossary could be read and populated through MCP but never designed through it. Each attribute is
The attribute identifiers the API demands are generated here, since it will not mint them itself and rejects the omission with a message that names neither the attribute nor the real problem. |
| update_glossary_term_typeA | Change a term type: rename it, or add, edit and remove its attributes. Attributes are matched to the existing ones by label, and an edit keeps that attribute's identifier — which matters more than it looks, because every term's stored values are filed under it. An attribute the caller does not mention is left alone; a label that does not exist yet is added. To rename one, give its Making an attribute |
| delete_glossary_term_typeA | Delete a term type. Refuses while terms still use the type, because deleting it takes their
attribute definitions with it. Check with |
| search_glossary_termsA | Free-text search of the business glossary — the way in when you know a word, not an id. Runs against the Information Catalog's Each hit carries both identifiers — |
| list_glossary_termsA | List glossary terms by structure — term type, parent, or name fragment. The counterpart to |
| get_glossary_termA | Get one business term in full, with its custom attributes named rather than hashed. The raw API returns Also returns |
| list_term_assetsA | List the data assets a business term is attached to — the columns that mean it. The authoritative answer to "where is this term actually used?", read
from the An empty result means the term is assigned to nothing, which is not
the same as no matching column existing — |
| list_table_termsA | List the business terms assigned to a table's columns. The reverse of Terms come from |
| create_glossary_termA | Create a business term in the SAS Business Glossary.
Values are validated before the call, so a mistake comes back naming the attribute and what it expected, instead of as an opaque HTTP 400. Terms are published by default. The underlying API defaults to
creating a draft, which nobody but its author can see; that is almost
never what a caller asking to "create a term" means, so this publishes
unless A term's name must be unique among its siblings (case-insensitively) and differ from its parent's; a clash is rejected, not merged. |
| update_glossary_termA | Update a business term's text, parent or custom attributes — and publish a draft. The glossary API replaces the whole term on update, so this reads the
current one first and merges your changes into it: omitting an argument
leaves that field alone rather than blanking it. Because the whole term is rewritten, every attribute the type marks required must hold a value — including ones made required after this term was created. That is checked before the call, and reported by name. A term's type cannot be changed after creation. Its parent can:
pass A draft is a different resource. A term left unpublished by
|
| import_glossary_termsA | Create many business terms, and their hierarchy, in one call. Building a hierarchy one term at a time means a call per term and a wait between levels, because a child needs the parent's id from the previous response. This uses the glossary's own bulk import instead: one request for the whole tree, with parents resolved by name rather than by id, so nothing has to be threaded through. Give each row a Attribute values take the same forms as The import runs as a job and reports rows individually. A row can
fail while the rest succeed, so the result carries The result names the term each row became. Two things the import does that a per-term create does not:
|
| delete_glossary_termA | Permanently delete a business term. The term goes, and with it every assignment to a column that referenced
it — the data keeps its columns but loses the documented meaning. Check
There is no cascade. A term that has children cannot be deleted at
all: the glossary refuses with |
| assign_glossary_termA | Assign a business term to a table column — the step that makes a term govern data. Creating a term only defines a word. This attaches it to the column that
carries it, and it is what |
| unassign_glossary_termA | Remove a business term's assignment from a table column. Deletes only the link: the term and the column both survive. Use
|
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
| debug_sas_log | Analyze a SAS log for errors, warnings, and notes with root-cause explanations and suggested fixes. |
| explore_dataset | Generate comprehensive SAS data-profiling code (CONTENTS, MEANS, FREQ, UNIVARIATE). |
| data_quality_check | Generate SAS code for a data quality assessment (completeness, uniqueness, validity). |
| statistical_analysis | Set up a complete SAS statistical analysis workflow with diagnostics. |
| optimize_sas_code | Review and optimize SAS code for performance, readability, or both. |
| explain_sas_code | Provide a block-by-block explanation of SAS code, tailored to skill level. |
| sas_macro_builder | Build a production-quality reusable SAS macro. |
| generate_report | Generate SAS ODS/PROC REPORT code for formatted output. |
| build_va_dashboard | Build a polished multi-page Visual Analytics dashboard from a CAS table, using the report-authoring tools (discover → shape → structure → polish → verify). |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
| sas-log:execute_sas_code | SAS log view for the execute_sas_code tool (MCP Apps). |
| data-grid:get_castable_data | Data grid view for the get_castable_data tool (MCP Apps). |
| data-grid:get_compute_table_data | Data grid view for the get_compute_table_data tool (MCP Apps). |
| term-editor:get_glossary_term | Glossary term editor view for the get_glossary_term tool (MCP Apps). |
| term-editor:get_glossary_term_type | Glossary term editor view for the get_glossary_term_type tool (MCP Apps). |
| sas-log:get_job_log | SAS log view for the get_job_log tool (MCP Apps). |
| import-preview:import_glossary_terms | Glossary import view for the import_glossary_terms tool (MCP Apps). |
| term-tree:list_glossary_terms | Glossary browser view for the list_glossary_terms tool (MCP Apps). |
| data-grid:query_data | Data grid view for the query_data tool (MCP Apps). |
| sas-log:submit_batch_job | SAS log view for the submit_batch_job tool (MCP Apps). |
TDQS
Scored across 92 tools
Each tool targets a distinct resource and action, with near-zero ambiguity even in dense areas. Overlapping data-access tools (query_data, get_castable_data, get_compute_table_data, execute_sas_code) are precisely differentiated and even cross-reference each other in descriptions. Catalog search vs glossary search, and upload_data vs upload_inline_data vs upload_file, all have clear boundaries.
The dominant verb_noun pattern (list_*, get_*, create_*, update_*, delete_*, lock_*, publish_*) is followed consistently within each domain, and CRUD families are easy to predict. The only deviation is the catalog_* group, which inverts the convention by placing the domain prefix first (catalog_search, catalog_run_agent) rather than as the object noun.
At 92 tools, the surface is far beyond the ideal range and well past the 25+ threshold for heaviness; it would take an agent many calls just to enumerate candidates. That said, the count is defensible: the server spans roughly a dozen genuinely distinct SAS Viya domains (rules, decision flows, CAS, compute, reports, catalog, glossary, ML), each of which is reasonably scoped on its own.
Every major domain has a closed lifecycle: full CRUD plus revisions and locking for business rules and decision flows, complete glossary management including term types and assignments, and report authoring with copy/delete/export. Minor gaps exist — CAS table alteration/removal is absent and MAS module management is read-only — but finding a table, scoring it, or tracing a governed term all have no dead ends.