Run a shared server
A shared server runs one AI Assist container as a service for your whole team. Developers connect to it over HTTPS and sign in with your identity provider, so the database password stays on the server, and every call is audited in one place under each person’s own identity.
A request travels like this:
-
The IDE sends MCP messages to
https://mcp.example.com/mcp, with an access token from your identity provider. -
nginx terminates TLS and forwards the request to the container on
127.0.0.1:8095. -
The container validates the token, runs the tool with its read-only database account, and writes an audit entry under the person’s identity.
Configure HTTP mode
Section titled “Configure HTTP mode”Start from the configuration file and add the settings for HTTP:
server: transport: http http: port: 8080 # inside the container path: /mcp allowed_origins: [] # web origins allowed to call the server
auth: issuer: https://login.example.com/ audience: api://aipeoplesoft-mcp scopes: metadata: aips.metadata.read # needed for every tool data: aips.data.read # also needed for run_select
audit: destination: /var/log/aipeoplesoft/audit.jsonlallowed_origins: the MCP specification requires servers to check theOriginheader, which protects them from malicious web pages. IDEs don’t send one; a request from a web page whose origin isn’t listed is rejected.auth: the server accepts only access tokens issued byissuerforaudience. There’s no HTTP mode without authentication.scopesnames the two permissions the server checks in each token: every tool needs the metadata scope, andrun_selectalso needs the data scope, so you can give data access to fewer people.audit: write the audit log to a file on a mounted volume, so it outlives the container.
Run the container
Section titled “Run the container”The server runs in the background with the serve --transport http command, published only on the
host’s loopback interface. Only nginx listens publicly.
Run the server as a dedicated, non-root user, such as aips, with a systemd service that
Podman’s Quadlet generates. As that user:
- Create the database password secret, as in Store the database password.
- Save
config.yamlas~/aips/config.yaml, and create the~/aips/auditdirectory. - Save this unit file as
~/.config/containers/systemd/aipeoplesoft-mcp.container:
[Unit]Description=aiPeopleSoft MCP server
[Container]Image=localhost/aipeoplesoft-mcp:1.0Exec=serve --transport httpPublishPort=127.0.0.1:8095:8080Volume=%h/aips/config.yaml:/etc/aipeoplesoft/config.yaml:ro,ZVolume=%h/aips/audit:/var/log/aipeoplesoft:ZSecret=aips_db_password,type=env,target=AIPS_DB_PASSWORD
[Service]Restart=always
[Install]WantedBy=default.targetThen start it. enable-linger keeps the user’s services running after you log out:
systemctl --user daemon-reloadsystemctl --user start aipeoplesoft-mcpsudo loginctl enable-linger aipsCheck it with systemctl --user status aipeoplesoft-mcp, and read its log with
journalctl --user -u aipeoplesoft-mcp.
Docker keeps the password in a file that Compose mounts into the container, and the server
reads it through AIPS_DB_PASSWORD_FILE. Protect the file with chmod 600.
services: aipeoplesoft-mcp: image: aipeoplesoft-mcp:1.0 command: ["serve", "--transport", "http"] ports: - "127.0.0.1:8095:8080" environment: AIPS_DB_PASSWORD_FILE: /run/secrets/aips_db_password secrets: - aips_db_password volumes: - ./config.yaml:/etc/aipeoplesoft/config.yaml:ro - ./audit:/var/log/aipeoplesoft restart: unless-stopped
secrets: aips_db_password: file: ./aips_db_password.txtStart it with docker compose up -d, and read its log with
docker compose logs aipeoplesoft-mcp.
Put nginx in front
Section titled “Put nginx in front”nginx terminates TLS and forwards requests to the container. Streamable HTTP can stream a response as it’s produced, so turn off response buffering for the MCP path:
server { listen 443 ssl; server_name mcp.example.com;
ssl_certificate /etc/ssl/mcp.example.com.pem; ssl_certificate_key /etc/ssl/mcp.example.com.key;
location /mcp { proxy_pass http://127.0.0.1:8095; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_buffering off; proxy_read_timeout 120s; }
# OAuth protected resource metadata, which tells IDEs # where to sign in location /.well-known/oauth-protected-resource { proxy_pass http://127.0.0.1:8095; }}proxy_read_timeout must be longer than the server’s query timeout, 30 seconds by default. Any
TLS certificate works, including a Cloudflare origin certificate when the site sits behind
Cloudflare’s proxy.
Sign in with your identity provider
Section titled “Sign in with your identity provider”A shared server uses MCP’s authorization specification, which is based on OAuth 2.1. The server is an OAuth resource server: it publishes metadata that names your identity provider, and accepts only access tokens issued for it.
-
Register the server with your identity provider as an API. Its identifier is the
audienceinconfig.yaml, such asapi://aipeoplesoft-mcp. -
Decide who can use it, for example by assigning your developers’ group to the API. Grant the data scope, which
run_selectneeds, only to the people who need to see data. -
Connect an IDE. The first time it connects, the IDE reads the server’s metadata, sends you to your identity provider to sign in, and stores the token.
Many identity providers only accept clients that are registered in advance. In that case, register
a client for each IDE your team uses, and give the IDE its client ID. For Claude Code, that’s the
--client-id option shown below.
Each call is audited under the identity in the token, and the rate limit of 60 calls per minute applies per person.
Connect IDEs to the shared server
Section titled “Connect IDEs to the shared server”claude mcp add --transport http aipeoplesoft \ https://mcp.example.com/mcpclaude mcp login aipeoplesoftIf your identity provider needs a registered client, add --client-id <client-id> to
claude mcp add.
{ "servers": { "aipeoplesoft": { "type": "http", "url": "https://mcp.example.com/mcp" } }}{ "mcpServers": { "aipeoplesoft": { "url": "https://mcp.example.com/mcp" } }}Check it’s working
Section titled “Check it’s working”Two requests from any machine show that nginx, the server, and sign-in are wired together:
# Without a token, the server refuses the request with 401# and a WWW-Authenticate header that points to its metadatacurl -si -X POST https://mcp.example.com/mcp | head -n 5
# The metadata names your identity providercurl -s https://mcp.example.com/.well-known/oauth-protected-resourceThen connect an IDE, sign in, and ask a first question, as in Before you start.
