{"id":51146384,"url":"https://github.com/sassoftware/sas-mcp-server","last_synced_at":"2026-06-26T03:01:35.920Z","repository":{"id":327262071,"uuid":"1095877321","full_name":"sassoftware/sas-mcp-server","owner":"sassoftware","description":"A Model Context Protocol (MCP) server for executing SAS code on SAS Viya environments","archived":false,"fork":false,"pushed_at":"2026-06-19T11:57:58.000Z","size":243,"stargazers_count":17,"open_issues_count":2,"forks_count":8,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-06-19T13:29:12.383Z","etag":null,"topics":["sas-aiml","sas-osp"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sassoftware.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":"SUPPORT.md","governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-11-13T16:27:06.000Z","updated_at":"2026-06-19T11:57:55.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/sassoftware/sas-mcp-server","commit_stats":null,"previous_names":["sassoftware/sas-mcp-server"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/sassoftware/sas-mcp-server","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sassoftware%2Fsas-mcp-server","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sassoftware%2Fsas-mcp-server/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sassoftware%2Fsas-mcp-server/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sassoftware%2Fsas-mcp-server/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sassoftware","download_url":"https://codeload.github.com/sassoftware/sas-mcp-server/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sassoftware%2Fsas-mcp-server/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34801014,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-26T02:00:06.560Z","response_time":106,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["sas-aiml","sas-osp"],"created_at":"2026-06-26T03:01:35.097Z","updated_at":"2026-06-26T03:01:35.896Z","avatar_url":"https://github.com/sassoftware.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# SAS MCP Server\n\nA Model Context Protocol (MCP) server for executing SAS code, training AutoML projects, scoring models and so much more for SAS Viya environments.\n\n## Features\n\n- 40+ Tools spanning the Analytics Life Cycle across SAS Viya\n- Prompt Templates for improving your SAS Code\n- OAuth2 authentication with PKCE flow\n- HTTP-based MCP server compatible with MCP clients\n\n## Articles \u0026 Videos\n\nHere you can find getting articles on how to use and integrate the SAS MCP Server in different tools and what to build with it:\n\n- [Connecting GitHub Copilot to SAS Viya with the SAS MCP Server](https://communities.sas.com/t5/SAS-Communities-Library/Connecting-GitHub-Copilot-to-SAS-Viya-with-the-SAS-MCP-Server/ta-p/987191)\n- [Putting the SAS MCP Server to Work in GitHub Copilot](https://communities.sas.com/t5/SAS-Communities-Library/Putting-the-SAS-MCP-Server-to-Work-in-GitHub-Copilot/ta-p/987193)\n- [Connecting Claude Code CLI to SAS Viya with the SAS MCP Server](https://communities.sas.com/t5/SAS-Communities-Library/Connecting-Claude-Code-CLI-to-SAS-Viya-with-the-SAS-MCP-Server/ta-p/988775)\n- [Putting the SAS MCP Server to Work in Claude Code CLI](https://communities.sas.com/t5/SAS-Communities-Library/Putting-the-SAS-MCP-Server-to-Work-in-Claude-Code-CLI/ta-p/988922)\n- [Integration with SAS Retrieval Agent Manager (RAM)](https://github.com/sassoftware/sas-retrieval-agent-manager-examples/tree/main/examples/container_mcp_servers/sas_mcp_server)\n\n## Getting Started\n### Prerequisites\n- Required\n    - [Python 3.12+](https://www.python.org/downloads) \n    - [uv 0.8+](https://github.com/astral-sh/uv)  \n    - [SAS Viya environment](https://www.sas.com/en_us/software/viya.html) with compute service\n    - Setup the Viya environment for MCP\n        - See [configuration.md](/examples/configuration.md)\n\n- Optional\n    - [Docker](https://docs.docker.com/engine/install): refer to [docker setup](/examples/docker/setup.md)\n\n### Installation\n\n1. Clone the repository:\n```sh\ngit clone \u003crepository-url\u003e\ncd sas-mcp-server\n```\n\n2. Install dependencies\n```sh\nuv sync\n```\n\nNOTE: This will by default create a virtual environment called .venv in the project's root directory. \n\nIf for some reason the virtual environment is not created, please run `uv venv` and then re-run `uv sync`.\n\n### Usage\n\n1. Configure environment variables:\n```sh\ncp .env.sample .env\n```\n\nEdit `.env` and set\n```sh\nVIYA_ENDPOINT=https://your-viya-server.com\n```\n\n2. Start the MCP server (see [Choosing a deployment mode](#choosing-a-deployment-mode) below):\n\n**Option A: HTTP mode** (pre-run the server, connect from MCP client)\n```sh\nuv run app\n```\nThe server will be available at `http://localhost:8134/mcp` by default. Authentication is handled via OAuth2 PKCE flow in the browser.\n\n**Option B: Stdio mode** (MCP client starts the server on demand)\n\nAuthenticate once. Two equivalent options:\n\n```sh\n# Option B1 — if you have the SAS Viya CLI installed:\nsas-viya auth loginCode\n\n# Option B2 — built-in helper, no external CLI needed (Viya 2022.11+):\nuv run sas-mcp-login\n```\n\nBoth flows write an access token to a local cache (`~/.sas/credentials.json` and `~/.sas-mcp-server/credentials.json` respectively); the stdio server reads whichever it finds. When the token expires, re-run the same command.\n\nThen configure your MCP client to launch the server directly (see below).\n\n**Option C: Docker / Podman** (containerized deployment)\n\nPull the pre-built image from GitHub Container Registry:\n```sh\ndocker pull ghcr.io/sassoftware/sas-mcp-server:latest\ndocker run -e VIYA_ENDPOINT=https://your-viya-server.com -p 8134:8134 ghcr.io/sassoftware/sas-mcp-server:latest\n```\n\nOr build locally from source:\n```sh\ndocker build -t sas-mcp-server .\ndocker run -e VIYA_ENDPOINT=https://your-viya-server.com -p 8134:8134 sas-mcp-server\n```\n\nAvailable image tags:\n- `latest` — most recent tagged release\n- `\u003cmajor\u003e.\u003cminor\u003e.\u003cpatch\u003e` (e.g. `1.0.0`) — specific release\n- `\u003cmajor\u003e.\u003cminor\u003e` (e.g. `1.0`) — latest patch of a minor release\n- `edge` — tip of `main` (unreleased, for testing)\n- `sha-\u003cshort\u003e` — pinned to a specific commit\n\n**Programmatic clients with a pre-existing Viya token**\n\nIf your caller already holds a Viya access token (e.g. an automation script that obtained one via the SAS Viya CLI), start the HTTP-mode server with `ALLOW_RAW_BEARER=true` and pass the token directly:\n\n```sh\ncurl -H \"Authorization: Bearer $VIYA_TOKEN\" http://localhost:8134/mcp ...\n```\n\nThe server validates the token against Viya's JWKS and uses it upstream as-is, bypassing the MCP JWT swap. The default OAuth2 PKCE flow keeps working alongside — both client types share the same `/mcp` endpoint.\n\n### Choosing a deployment mode\n\n| | **HTTP** | **Stdio** | **Docker** |\n|---|---|---|---|\n| **How it runs** | Long-running server you start separately | MCP client spawns it on demand | Containerized HTTP server |\n| **Authentication** | OAuth2 PKCE flow (browser popup) | Cached token via `sas-viya` CLI or `sas-mcp-login` | OAuth2 PKCE flow (browser popup) |\n| **Best for** | Multi-user or shared setups; production-like environments | Single-user local development; quick experimentation | Team deployments; CI/CD; environments without Python installed |\n| **Requires** | Python + uv | Python + uv (+ optional `sas-viya` CLI) | Docker or Podman only |\n| **Credentials stored?** | No — user authenticates interactively | No — only an access token (not a password) is cached | No — user authenticates interactively |\n| **MCP client config** | Point client to `http://localhost:8134/mcp` | Client runs `uv run app-stdio` | Point client to `http://host:8134/mcp` |\n\n**Quick guidance:**\n- **Starting out or exploring?** Use **stdio** — one `sas-viya auth loginCode` or `uv run sas-mcp-login`, then your MCP client manages the server lifecycle.\n- **Need secure, interactive auth?** Use **HTTP** — no stored passwords, each user authenticates via browser.\n- **Deploying for a team or on a server?** Use **Docker** — portable, no Python dependency on the host, easy to integrate with orchestrators.\n- **Using Gemini CLI?** Use **stdio** — Gemini CLI does not support HTTP mode or browser-based OAuth. See [Gemini CLI configuration](examples/configuration.md#gemini-cli).\n\n### Available Tools\n\n#### Code Execution\n- **execute_sas_code**: Execute SAS code snippets and retrieve execution results (log and listing output). Runs in a reusable, per-user compute session that is kept warm across calls, so SAS state (WORK tables, macro variables, assigned librefs) persists between successive calls — use **reset_compute_session** to start fresh.\n\n#### Data Governance (Metadata Discovery \u0026 Profiling)\n- **catalog_search**: Search the catalog for assets (tables, columns, reports, …) using the SAS catalog search grammar (free text, facets like `AssetType:Report`, ranges). Each hit carries a `resource_uri` you can hand to the matching tool (e.g. `get_report`, `get_castable_data`).\n- **catalog_search_helper**: Discover how to query the catalog — list the available facets, or the valid values for one facet — so you can build precise `catalog_search` queries.\n- **catalog_find_instance**: Resolve the catalog *instance* for a source-asset `resource_uri`, bridging a search hit to the profiling and download tools without handling an instance id by hand.\n- **catalog_run_adhoc_analysis**: Submit an ad-hoc profiling job for a table. NLP enrichment (language, sentiment, semantic IDs) is on by default, populating `informationPrivacy`, `nlpTerms`, `nlpTags`, and `mostImportantFields`.\n- **catalog_get_adhoc_analysis**: Poll a profiling job and cross-check the target instance, reporting `profile_ready` once results have landed on the asset — so a download isn't fired too early.\n- **catalog_download_table_profile**: Download a table's data dictionary and column profile as CSV, identified by either `instance_id` or `resource_uri`.\n- **catalog_list_agents**: List the catalog's discovery agents (the crawlers that populate metadata).\n- **catalog_run_agent**: Start a discovery agent run (asynchronous) to crawl its data source and refresh catalog metadata.\n- **catalog_get_agent_history**: Inspect an agent's run history — status and how much metadata each run enumerated/added/updated/removed.\n\n#### Data Discovery (CAS Management)\n- **list_cas_servers**: List available CAS servers\n- **list_caslibs**: List CAS libraries on a server\n- **list_castables**: List tables in a CAS library\n- **list_source_tables**: List source tables not yet loaded into memory (candidates for promotion)\n- **get_castable_info**: Get table metadata (row count, columns, size)\n- **get_castable_columns**: Get column names, types, labels, formats\n- **get_castable_data**: Fetch sample rows from a CAS table\n\n#### Data Operations \u0026 Files\n- **upload_data**: Upload a data file into a CAS table — read **server-side** so the data never passes through the model's context — from `file_path` (the server reads it off disk) or `url` (the server fetches it and converts it to the multipart upload the endpoint requires). Ingests the formats the casManagement `uploadTable` API accepts — csv, tsv (csv + tab delimiter), xls, xlsx (single sheet), sas7bdat, sashdat — auto-detected from the extension or set with `data_format`. parquet is not accepted by that endpoint and is rejected up front with guidance (load via a path-based caslib + `promote_table_to_memory`, or convert to csv/sas7bdat).\n- **upload_inline_data**: Create a *small* CAS table from inline csv/tsv text passed as a string (a lookup/mapping table the model builds on the fly, or a quick test table). The payload travels through the model's context, so it's for tiny tables only — use **upload_data** for files or anything larger.\n- **promote_table_to_memory**: Load a source table into memory at global scope (idempotent)\n- **list_files**: List files in the Viya Files Service\n- **upload_file**: Upload a file to Viya Files Service\n- **download_file**: Download file content\n\n#### Reports \u0026 Visualization\n- **list_reports**: List Visual Analytics reports\n- **get_report**: Get report metadata and definition\n- **get_report_image**: Render a report section as an image\n\n#### Batch Jobs\n- **submit_batch_job**: Submit a SAS job for async execution\n- **get_job_status**: Check job state\n- **list_jobs**: List recent/running jobs\n- **cancel_job**: Cancel a running job\n- **get_job_log**: Retrieve job log\n\n#### Model Management \u0026 Scoring\n- **list_ml_projects**: List AutoML projects\n- **create_ml_project**: Create a new AutoML project from a loaded, global-scope CAS table (caslib + table + optional CAS server)\n- **run_ml_project**: Run pipeline automation\n- **list_registered_models**: List models in repository\n- **list_models_and_decisions**: List published MAS modules\n- **score_data**: Score data against a published model\n\n#### Compute Contexts \u0026 Code Execution\n- **list_compute_contexts**: List available compute contexts\n- **list_compute_libraries**: List the SAS libraries (librefs) assigned in a compute context\n- **list_compute_tables**: List the tables in a SAS library within a compute context\n- **list_compute_columns**: List the columns of a table in a SAS library\n- **reset_compute_session**: Delete the cached compute session for a context, discarding its SAS state and forcing a fresh session on the next call\n\n### Prompt Templates\n\n- **debug_sas_log**: Analyze SAS log for errors with root-cause explanations\n- **explore_dataset**: Generate data-profiling SAS code\n- **data_quality_check**: Generate DQ assessment code\n- **statistical_analysis**: Set up a statistical workflow with diagnostics\n- **optimize_sas_code**: Review and optimize SAS code\n- **explain_sas_code**: Block-by-block code explanation\n- **sas_macro_builder**: Build production-quality SAS macros\n- **generate_report**: Generate ODS/PROC REPORT code\n\n## MCP Client Configuration\n\nExample configurations are provided in the `examples/` folder. Below are quick-start snippets for common clients.\n\n### VS Code / Cursor / Claude Code (`.vscode/mcp.json`)\n\n**HTTP mode** (requires `uv run app` running separately):\n```json\n{\n    \"servers\": {\n        \"sas-execution-mcp\": {\n            \"url\": \"http://localhost:8134/mcp\",\n            \"type\": \"http\"\n        }\n    }\n}\n```\n\n**Stdio mode** (starts the server on demand):\n```json\n{\n    \"servers\": {\n        \"sas-execution-mcp\": {\n            \"command\": \"uv\",\n            \"args\": [\"run\", \"app-stdio\"],\n            \"cwd\": \"${workspaceFolder}\"\n        }\n    }\n}\n```\n\n### Gemini CLI (`.gemini/settings.json`)\n\nGemini CLI only supports stdio mode. Add to your `~/.gemini/settings.json` or project-level `.gemini/settings.json`:\n\n```json\n{\n    \"mcpServers\": {\n        \"sas-viya-mcp\": {\n            \"command\": \"uv\",\n            \"args\": [\"run\", \"app-stdio\"],\n            \"cwd\": \"/path/to/sas-mcp-server\",\n            \"timeout\": 60000\n        }\n    }\n}\n```\n\n\u003e **Note:** The `timeout` field (in milliseconds) is important — SAS Viya API calls can take longer than the Gemini CLI default of 10 seconds. A value of `60000` (60s) is recommended. Set `cwd` to the absolute path of your `sas-mcp-server` checkout.\n\n## Example\n\nExecute SAS code through the MCP tool:\n```sas\ndata work.students;\ninput Name $ Age Grade $;\ndatalines;\nAlice 20 A\nBob 22 B\n;\nrun;\n\nproc print data=work.students;\nrun;\n```\n---\n\n**For more details, configuration options, and deployment options, please refer to the **examples** folder and follow the instructions listed there.**\n\n## Testing\n\nThe project includes two layers of tests: **unit tests** (fast, no credentials required) and **integration tests** (run against a real SAS Viya instance).\n\n### Running Unit Tests\n\nUnit tests verify tool schemas, request payloads, and internal logic without making any network calls:\n\n```sh\n./run_tests.sh\n```\n\nOr directly via pytest:\n\n```sh\nuv run python -m pytest -m \"not integration\" -v\n```\n\n### Running Integration Tests\n\nIntegration tests call every tool against a live Viya environment. They require credentials, which can be provided via CLI arguments or `.env`:\n\n**Using `.env`** (set `VIYA_ENDPOINT`, `VIYA_USERNAME`, `VIYA_PASSWORD`):\n```sh\n./run_tests.sh --integration\n```\n\n**Using CLI arguments:**\n```sh\n./run_tests.sh --integration \\\n    --endpoint https://your-viya-server.com \\\n    --username youruser \\\n    --password yourpassword\n```\n\n**Integration tests only** (skip unit tests):\n```sh\n./run_tests.sh --integration-only\n```\n\n**Binary upload formats.** The `upload_data` Excel integration test generates its `.xlsx`\nfixture with `openpyxl`. Install the optional group so it runs instead of `importorskip`-ing:\n`uv sync --group test-formats`. (csv, tsv, and `file_path`/`data_format` coverage needs no\nextra deps.) Generating a `sas7bdat`/`sashdat` fixture requires SAS itself, so those two\nformats are covered by unit-level payload tests only, not live.\n\nEvery one of the 42 tools and 8 prompt templates has an integration test, enforced by the\n`test_every_tool_has_integration_coverage` / `test_every_prompt_has_integration_coverage`\nguards — adding a new tool or prompt without integration coverage fails the suite. The\nresource-dependent tests discover real targets on the instance: `score_data` scores the most\nrecently modified MAS module (discovering a real step and its inputs), and `run_ml_project`\nre-runs the most recently modified `completed` ML project. They `skip` only if the instance\nhas no such resource at all.\n\n**In CI:** the `.github/workflows/integration.yml` workflow runs this suite on demand\n(manual dispatch, or by adding the `run-integration` label to a PR) using repository\nsecrets, and publishes the results back to the PR as a status check, a sticky comment, and\na downloadable JUnit artifact. Result files are written to `reports/` (git-ignored) and are\nnever committed.\n\n**Locally (attach results to a PR yourself):** run with `--report` to write the JUnit XML\nand a Markdown summary into `reports/` (git-ignored), then post them to a PR with the GitHub\nCLI — no commit, no CI required:\n\n```sh\n./run_tests.sh --integration-only --report\ngh pr comment \u003cPR\u003e --body-file reports/integration-summary.md   # summary table as a comment\ngh gist create reports/integration.xml                          # full XML as a linkable gist\n```\n\n\u003e GitHub has no API/CLI to attach a binary file to a PR (drag-and-drop upload is browser-only),\n\u003e so the summary is posted as a comment and the raw XML is shared via a gist link or pasted in a\n\u003e collapsed `\u003cdetails\u003e` block. To produce the canonical Actions *artifact* from your machine\n\u003e instead, trigger the workflow remotely: `gh workflow run integration.yml`.\n\n### Test Structure\n\n| File | Description |\n|---|---|\n| `tests/test_tool_payloads.py` | Payload assertions for all 42 tools (URL paths, JSON body, query params, headers) plus error-path coverage |\n| `tests/test_integration.py` | End-to-end workflow tests against a real Viya instance |\n| `tests/test_tools.py` | Unit tests for the generic Viya REST helpers in `viya_client` (`get_json`, `post_json`, `make_client`, …) |\n| `tests/test_viya_utils.py` | Unit tests for Viya compute session and job orchestration |\n| `tests/test_mcp_server.py` | Unit tests for the HTTP auth middleware, health route, and token getter |\n| `tests/test_config.py` | Unit tests for configuration loading |\n| `tests/test_config_oauth.py` | Unit tests for `PermissiveOAuthProxy` raw-bearer handling |\n| `tests/test_auth_login.py` | Unit tests for the `sas-mcp-login` OAuth/PKCE helper |\n| `tests/test_stdio_server.py` | Unit tests for stdio token resolution and the device-code flow |\n| `tests/test_env.py` | Unit tests for the `env_bool` helper |\n| `tests/test_prompts.py` | Unit tests for prompt template rendering |\n\n## Contributing\nMaintainers are accepting patches and contributions to this project. Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details about submitting contributions to this project.\n\n## License \u0026 Attribution\n\nExcept for the the contents of the `/static` folder, this project is licensed under the [Apache 2.0 License](LICENSE). \nElements in the `/static` folder are owned by SAS and are not released under an open source license. \nSAS and all other SAS Institute Inc. product or service names are registered trademarks or trademarks of SAS Institute Inc. in the USA and other countries. ® indicates USA registration.\n\nSeparate commercial licenses for SAS software (e.g., SAS Viya) are not included and are required to use these capabilities with SAS software.\n\nAs with any container image, direct and indirect dependencies are governed by their own licenses.\nUsers of the published container image are responsible for ensuring that their use complies with all applicable licenses.\n\nAll third-party trademarks referenced belong to their respective owners and are only used here for identification and reference purposes, and not to imply any affiliation or endorsement by the trademark owners.\n\n## Third-Party Dependencies\n\nThis project requires the following dependencies.\n\n| Dependency | License |\n| ---------- | ------- |\n| Python | [Python Software License](https://docs.python.org/3/license.html) |\n| FastMCP | [Apache License 2.0](https://github.com/PrefectHQ/fastmcp/blob/main/LICENSE) |\n| uvicorn | [BSD 3-Clause License](https://github.com/Kludex/uvicorn/blob/main/LICENSE.md) |\n| starlette | [BSD 3-Clause License](https://github.com/Kludex/starlette/blob/main/LICENSE.md)\n| httpx | [MIT License](https://github.com/projectdiscovery/httpx/blob/dev/LICENSE.md) | \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsassoftware%2Fsas-mcp-server","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsassoftware%2Fsas-mcp-server","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsassoftware%2Fsas-mcp-server/lists"}