Adios
Guides

Build it. Host it. Connect it.

MCP Server Hosting

Start with a working Orders API and MCP server, or build them step by step. Choose JavaScript, Python or Go, explore the authenticated tools in an Adios workspace, then adapt the example to your own API.

Build a read-only order lookup: ask for order demo-1001 and get its shipping status. The downloadable examples include source, dependency files, deployment manifests and a client check. Choose one language; you do not need three MCP servers.

Start with a running example

Launch it, then make it yours

Create the Orders API, a PostgreSQL database and your chosen MCP server in Adios. We upload the source, configure separate JWT credentials and start development previews.

Launch to Adios →

No account yet? Create one and return to this example. Select a team and review the resources before launching. The full example needs a paid plan with room for two workspaces and a database.

Build it step by step

Follow the manual guide

Design the API and schema, add JWT authentication, write the MCP tools and run them in your own development workspaces. Each step includes source and commands.

Follow the manual walkthrough →

Use the running example to explore first. OAuth for user sign-in and production deployment are later steps.

Design the API around a real use case

Define the API boundary: validate requests, enforce permissions and return structured results.

Start with what the API is for. Our example lets an internal dashboard, a support assistant and an automation check an order’s shipping status. All three use the same business operation. The API owns the data and access rules; MCP gives an AI client a way to discover and call that operation.

Define the first release as read-only: an authorized caller supplies an order ID and receives its ID and shipping status. Keep customer addresses, payment details and internal notes out of this response. Creating orders, issuing refunds and changing shipments belong to separate endpoints and permissions.

  • Who calls it? A known user or service identity with an assigned tenant.
  • What can they do? Read orders belonging to that tenant with the orders:read permission.
  • What is the API responsible for? Validate the request, verify the caller, enforce ownership and return a stable response.
  • What is MCP responsible for? Describe a narrow tool, validate its input and call the API as the verified caller.

Write this contract before choosing an SDK. It keeps the website, integrations and AI tools on the same permission model instead of giving an AI client direct database access.

Design the database schema and ownership rules

Use PostgreSQL for this example. A tenant represents a shop or organization. A principal links a verified token subject to a tenant and can be disabled. An order belongs to one tenant. The API uses that relationship to decide whether a caller may read it.

TablePurposeAccess rule
tenantsOwn the organization’s dataReferenced by principals and orders
principalsMap a verified subject to a tenantOnly active identities can read data
ordersStore the shipping statusThe caller’s tenant must match the order’s tenant
api/schema.sqlDownload file
-- Demo bootstrap only. Use versioned migrations for an existing production DB.
CREATE TABLE IF NOT EXISTS tenants (
  id uuid PRIMARY KEY,
  name text NOT NULL
);
CREATE TABLE IF NOT EXISTS principals (
  subject text PRIMARY KEY,
  tenant_id uuid NOT NULL REFERENCES tenants(id),
  active boolean NOT NULL DEFAULT true
);
CREATE TABLE IF NOT EXISTS orders (
  tenant_id uuid NOT NULL REFERENCES tenants(id),
  id varchar(64) NOT NULL CHECK (id ~ '^[A-Za-z0-9-]{1,64}$'),
  status text NOT NULL CHECK (status IN ('pending', 'shipped', 'delivered')),
  created_at timestamptz NOT NULL DEFAULT now(),
  PRIMARY KEY (tenant_id, id)
);
-- Fictional data; ownership comes from the verified subject, not tool input.
INSERT INTO tenants (id, name) VALUES
  ('11111111-1111-4111-8111-111111111111', 'Demo shop'),
  ('22222222-2222-4222-8222-222222222222', 'Other shop')
ON CONFLICT DO NOTHING;
INSERT INTO principals (subject, tenant_id) VALUES
  ('demo-client', '11111111-1111-4111-8111-111111111111'),
  ('other-client', '22222222-2222-4222-8222-222222222222')
ON CONFLICT DO NOTHING;
INSERT INTO orders (tenant_id, id, status) VALUES
  ('11111111-1111-4111-8111-111111111111', 'demo-1001', 'shipped'),
  ('22222222-2222-4222-8222-222222222222', 'other-1002', 'pending')
ON CONFLICT DO NOTHING;

The composite order key supports the same order ID in different tenants. Foreign keys prevent orphan records, and the status constraint prevents unsupported states. The lookup uses the verified subject to join its tenant to the order; the caller cannot choose a tenant in the tool input.

The bootstrap includes two fictional tenants so you can test isolation: demo-client can read demo-1001, while other-client owns other-1002. The demo runs an idempotent bootstrap at startup. For an existing production database, use versioned migrations and separate provisioning from application requests.

Define endpoints, responses and JWT authentication

The public API exposes an order lookup endpoint. A successful response is a small JSON object with id and status. Keep the schema stable so both your website and MCP tool can rely on it.

API contract
GET /orders/demo-1001
Authorization: Bearer <API-audience JWT>

200  { "id": "demo-1001", "status": "shipped" }
401  missing, invalid or expired JWT
403  valid JWT without orders:read
404  unknown order, inactive identity or another tenant’s order
503  database unavailable

JWT authentication is the minimum in every implementation below. A bearer header transports the token; it is not sufficient to compare it to a random string or merely decode it. Verify its signature with an allowed algorithm, require the expected issuer and audience, check expiry and subject, and enforce the scope before touching the database.

Demo MCP token claims
{
  "iss": "orders-demo",
  "aud": "orders-mcp",
  "sub": "demo-client",
  "scope": "orders:read",
  "iat": 1791498000,
  "exp": 1791498900
}

The controlled demo uses HS256 with separate API and MCP signing secrets. Its operator-only script issues a short-lived MCP JWT. The MCP server verifies it, then signs a new five-minute JWT for the orders-api audience with the same verified subject. The API verifies that second token and looks up the subject’s tenant in PostgreSQL.

This makes MCP a trusted gateway with API signing authority. Never distribute signing secrets to clients or expose the token-issuing script as a public endpoint. For users outside this trust boundary, prefer an OAuth provider issuing asymmetric JWTs and verify its public keys; the OAuth section shows the resource-server side. JWT is a token format, while OAuth handles delegated authorization and sign-in flows.

Do not accept a tenant ID from the client as proof of ownership. Do not forward an MCP-audience token to the API. Keep tokens short-lived, rotate keys and use HTTPS in production. When supporting multiple identity providers, map the pair of issuer and subject to a canonical principal instead of assuming subjects are globally unique.

Operator-only demo JWT issuer
issue-token.mjsDownload file
import { createHmac } from "node:crypto";

// Operator-only demo issuer. Never expose this script or its key to end users.
const audience = process.argv[2] || "orders-mcp";
if (!["orders-mcp", "orders-api"].includes(audience))
  throw new Error("Unknown audience");
const secret =
  process.env[audience === "orders-api" ? "API_JWT_SECRET" : "MCP_JWT_SECRET"];
if (!secret || secret.length < 32)
  throw new Error("Set the audience's JWT signing secret");
const encode = (value) =>
  Buffer.from(JSON.stringify(value)).toString("base64url");
const now = Math.floor(Date.now() / 1000);
const data = `${encode({ alg: "HS256", typ: "JWT" })}.${encode({ iss: "orders-demo", aud: audience, sub: "demo-client", scope: "orders:read", iat: now, exp: now + 900 })}`;
process.stdout.write(
  `${data}.${createHmac("sha256", secret).update(data).digest("base64url")}`,
);

Implement the API before adding MCP

Unzip the examples and open api/. The Node.js implementation uses jose to verify JWTs and pg to query PostgreSQL. Install the locked dependencies with npm ci. The parameterized query joins the verified subject to its tenant and returns only that tenant’s order.

API dependencies and start command
api/package.jsonDownload file
{
  "name": "orders-api",
  "private": true,
  "type": "module",
  "scripts": {
    "start": "node init-db.mjs && node server.mjs"
  },
  "dependencies": {
    "jose": "^6.1.0",
    "pg": "8.23.1"
  }
}
api/server.mjsDownload file
import http from "node:http";
import { jwtVerify } from "jose";
import pg from "pg";

const secret = process.env.API_JWT_SECRET;
if (!secret || secret.length < 32 || !process.env.DATABASE_URL) {
  throw new Error(
    "Set DATABASE_URL and an API_JWT_SECRET of at least 32 characters",
  );
}
const key = new TextEncoder().encode(secret);
const pool = new pg.Pool({
  connectionString: process.env.DATABASE_URL,
  max: 5,
  connectionTimeoutMillis: 5000,
  statement_timeout: 5000,
});
pool.on("error", () => console.error("Database connection failed"));
await pool.query("SELECT 1");
const server = http.createServer(async (req, res) => {
  const send = (status, data) => {
    res.writeHead(status, {
      "Content-Type": "application/json",
      "Cache-Control": "no-store",
    });
    res.end(JSON.stringify(data));
  };
  if (req.url === "/healthz" && req.method === "GET") {
    try {
      await pool.query("SELECT 1");
      return send(200, { ok: true });
    } catch {
      return send(503, { ok: false });
    }
  }
  let principal;
  try {
    const match = /^Bearer (\S+)$/.exec(req.headers[(process.env.API_AUTH_HEADER || "Authorization").toLowerCase()] || "");
    if (!match) throw new Error("Missing access token");
    const { payload } = await jwtVerify(match[1], key, {
      issuer: "orders-demo",
      audience: "orders-api",
      algorithms: ["HS256"],
      requiredClaims: ["sub", "exp"],
    });
    if (typeof payload.sub !== "string" || !payload.sub)
      throw new Error("Missing subject");
    if (
      typeof payload.scope !== "string" ||
      !payload.scope.split(" ").includes("orders:read")
    ) {
      return send(403, { error: "orders:read permission required" });
    }
    principal = payload.sub;
  } catch {
    res.setHeader("WWW-Authenticate", 'Bearer error="invalid_token"');
    return send(401, { error: "Invalid or expired access token" });
  }
  const match = /^\/orders\/([a-zA-Z0-9-]{1,64})$/.exec(req.url || "");
  if (req.method !== "GET" || !match) return send(404, { error: "Not found" });
  try {
    const result = await pool.query(
      `SELECT o.id, o.status FROM orders o
       JOIN principals p ON p.tenant_id = o.tenant_id
       WHERE p.subject = $1 AND p.active = true AND o.id = $2`,
      [principal, match[1]],
    );
    // The same 404 covers unknown IDs and orders outside the caller's tenant.
    return result.rows[0]
      ? send(200, result.rows[0])
      : send(404, { error: "Order not found" });
  } catch {
    return send(503, { error: "Order service unavailable" });
  }
});
server.requestTimeout = 10_000;
server.listen(
  Number(process.env.PORT || 8081),
  process.env.HOST || "127.0.0.1",
);
Initialize the demo database
api/init-db.mjsDownload file
import { readFile } from "node:fs/promises";
import pg from "pg";

if (!process.env.DATABASE_URL) throw new Error("Set DATABASE_URL");
const deadline = Date.now() + 45_000;
let client;
while (true) {
  client = new pg.Client({
    connectionString: process.env.DATABASE_URL,
    connectionTimeoutMillis: 5000,
    statement_timeout: 10000,
  });
  try {
    await client.connect();
    break;
  } catch {
    await client.end().catch(() => {});
    if (Date.now() >= deadline)
      throw new Error(
        "Database unavailable. Check its private endpoint and credentials.",
      );
    await new Promise((resolve) => setTimeout(resolve, 1000));
  }
}
try {
  await client.query(
    await readFile(new URL("./schema.sql", import.meta.url), "utf8"),
  );
  console.log("Demo database initialized");
} catch {
  throw new Error("Could not initialize the demo database");
} finally {
  await client.end();
}

The API returns the same 404 for missing and inaccessible orders, so a caller cannot use the endpoint to discover another tenant’s order IDs. Its public health route checks the database connection without exposing data or requiring a JWT.

If you already have an API, keep its data model and ownership checks. Adapt the endpoint and response mapping in the MCP handler. Use a fixed upstream origin and endpoint rather than accepting arbitrary URLs from tool input.

Add MCP as a tool layer over the API

Model Context Protocol (MCP) gives an AI client a standard way to discover tools and call them. Your server describes a tool, accepts validated input, performs the operation and returns a result. Your REST API can keep serving your website and other integrations.

Request path
AI client → HTTPS /mcp → get_order({ order_id: "demo-1001" })
          → GET /orders/demo-1001 → { id: "demo-1001", status: "shipped" }

Client → MCP: MCP_ACCESS_TOKEN, or a user OAuth access token
MCP → API:    newly signed API JWT with the verified caller’s subject

The server verifies the client JWT before creating a tool handler. Each API call uses a new JWT with a different audience and the verified subject. The API keeps enforcing the database ownership rules; MCP does not bypass them.

Use Streamable HTTP for a remotely hosted server. Stdio is useful when a desktop client launches a local process. These HTTP examples are stateless and return JSON; they do not require session storage or a long-lived event stream. They expose /mcp for the protocol and a public /healthz for the runtime probe.

You need PostgreSQL and Node.js 24 for the sample API, plus Node.js 24, Python 3.13 or Go 1.25 for your selected MCP server. Install the Adios CLI and have an Adios team ready. These examples use the official JavaScript SDK 1.32.1, Python SDK 1.30.0 and Go SDK 1.8.0; avoid mixing them with different major-version tutorials.

Choose one language guide

The API contract and permission model stay the same. Continue with the language you use: each subguide contains its own server code, dependencies, workspace checks, and Adios deployment manifest.

All three implementations call the shared API and verify JWTs. The JavaScript guide also includes the optional OAuth resource-server gate; Python and Go describe the authorization work needed before supporting user sign-in.

Develop in an Adios workspace

Source, previews and logs stay together in an Adios workspace.

Use an Adios development team and managed PostgreSQL. Your computer only uploads source and runs the client check; the API and MCP processes run in Adios workspaces.

Terminal
adios login
adios teams
adios teams switch YOUR_DEV_TEAM_ID
export POSTGRES_PASSWORD="$(openssl rand -hex 32)"
export API_JWT_SECRET="$(openssl rand -hex 32)"
export MCP_JWT_SECRET="$(openssl rand -hex 32)"
printf '%s' "$POSTGRES_PASSWORD" | adios secrets set ORDERS_DB_PASSWORD --stdin
printf '%s' "$API_JWT_SECRET" | adios secrets set ORDERS_API_JWT_SECRET --stdin
printf '%s' "$MCP_JWT_SECRET" | adios secrets set ORDERS_MCP_JWT_SECRET --stdin
adios databases create orders-dev-db --template postgres:16 \
  --region de --database orders --user orders_api \
  --password-secret ORDERS_DB_PASSWORD

Wait for the database to be ready. Copy its connection URL from the database details in Console and save it as ORDERS_DEV_DATABASE_URL using secret input. Use the assigned host and verify that the workspace region can reach it.

Terminal
adios secrets set ORDERS_DEV_DATABASE_URL --stdin
# Paste the database connection URL, then finish stdin with Ctrl-D.
cp api/adios.dev.yaml api/adios.yaml
adios ws create --name orders-api-dev --json
# Copy workspace_id from the response:
export API_WORKSPACE_ID=YOUR_API_WORKSPACE_ID
(cd api && adios sync "$API_WORKSPACE_ID")
adios ws run start "$API_WORKSPACE_ID" --wait --json
adios ws get "$API_WORKSPACE_ID"

Create a standalone workspace first, then sync source into it. Creating a workspace from a project path can require an existing deployed app; this standalone flow starts development without an app release.

api/adios.dev.yamlDownload file
name: orders-api-dev
region: de
build_cmd: npm ci
start_cmd: npm start
runtime:
  name: node@24
  port: 8081
  health_path: /healthz
env:
  API_AUTH_HEADER: X-Orders-Token
  HOST: 0.0.0.0
  PORT: "8081"
  DATABASE_URL: secret://ORDERS_DEV_DATABASE_URL
  API_JWT_SECRET: secret://ORDERS_API_JWT_SECRET

The API preview initializes the demo schema and starts the Node.js process. Copy its actual HTTPS preview origin from the run result or Console. Keep this terminal open so the client issuer retains the signing keys.

Terminal
export API_BASE_URL=https://YOUR-API-PREVIEW-HOST
export API_ACCESS_TOKEN="$(node issue-token.mjs orders-api)"
curl --fail "$API_BASE_URL/healthz"
curl --fail -H "Authorization: Bearer $API_ACCESS_TOKEN" \
  "$API_BASE_URL/orders/demo-1001"

Continue with your language guide to create the MCP workspace. Update files with adios sync and restart the preview to apply them. A preview is a development runtime, not a promoted app release.

Choose a language implementation →

Stop previews from their workspace controls when finished. The managed database has its own lifecycle; stopping a preview does not stop or delete it.

Terminal
adios ws run stop "$API_WORKSPACE_ID"

Add OAuth for user sign-in and delegated access

The JWT examples cover a controlled integration with operator-issued tokens. They do not implement user login, consent or refresh. Add OAuth when people connect their own accounts or when the client requires the MCP authorization flow. This example’s OAuth mode accepts provider-issued JWT access tokens.

Your MCP server is the protected resource. An authorization server handles login, consent and token issuance. Your existing OAuth provider can do this if it supports the discovery and client onboarding your MCP clients need. Adios hosts the app and stores secrets; CLI login does not add OAuth to your app automatically.

  1. Publish resource metadata at /.well-known/oauth-protected-resource/mcp, naming your MCP URL, authorization server and supported scopes.
  2. Return 401 for an unauthenticated request with a WWW-Authenticate header pointing to that metadata.
  3. The client discovers the authorization server’s OAuth or OpenID Connect metadata. Arrange preregistration, client metadata documents or dynamic registration as supported by the provider and client.
  4. The user signs in and grants access. The client uses an authorization-code flow with PKCE and requests a token for your MCP resource.
  5. Validate the access token’s signature or introspection result, issuer, audience, expiry and scopes. Map its subject to your user and tenant before running a tool.

Start with orders:read. Keep write tools behind separate permissions and explicit confirmation where the client supports it. An ID token is not an API access token, and a valid login does not grant access to every customer’s orders.

The JavaScript subguide includes a complete optional RS256 JWT resource-server gate. It publishes resource metadata and verifies provider tokens, but does not implement login, consent, client registration, or token issuance.

Follow the JavaScript OAuth implementation →

Python and Go need an equivalent provider token verifier and resource metadata before accepting user OAuth tokens. A token value alone does not make the JWT demos OAuth-enabled.

A third-party API may also need a separate OAuth connection. Store its refresh tokens securely, associate them with the correct user and request only the permissions your tools need. Provider client secrets belong in Adios secrets; issuer and public JWKS URLs are ordinary configuration.

Protocol reference: MCP authorization specification.

Deploy PostgreSQL, the API and MCP on Adios

Deploy two apps: orders-api and orders-mcp. They can be released independently. Use the same Adios team for their secret references. The example region is de; choose an available region appropriate for your users.

Sign in and create the secrets

Choose the release team and generate fresh signing secrets and a database password for it. Keep release data and credentials separate from development. Adios login authenticates the CLI to the platform.

Terminal
adios login
adios teams
adios teams switch YOUR_RELEASE_TEAM_ID
export API_JWT_SECRET="$(openssl rand -hex 32)"
export MCP_JWT_SECRET="$(openssl rand -hex 32)"
export POSTGRES_PASSWORD="$(openssl rand -hex 32)"
printf '%s' "$API_JWT_SECRET" | adios secrets set ORDERS_API_JWT_SECRET --stdin
printf '%s' "$MCP_JWT_SECRET" | adios secrets set ORDERS_MCP_JWT_SECRET --stdin
printf '%s' "$POSTGRES_PASSWORD" | adios secrets set ORDERS_DB_PASSWORD --stdin

Deploy the API first

The API manifest declares a managed Postgres resource. Adios provisions it and injects DATABASE_URL into the API runtime. The database password is a secret reference. The demo’s start command initializes its schema before starting the API; use your normal migration process for a production database.

api/adios.release.yamlDownload file
name: orders-api
region: de
replicas: 1
build_cmd: npm ci
start_cmd: npm start
runtime:
  name: node@24
  port: 8081
  health_path: /healthz
resources:
  - name: db
    template: postgres:16
    database: orders
    username: orders_api
    password: secret://ORDERS_DB_PASSWORD
env:
  HOST: 0.0.0.0
  PORT: "8081"
  API_JWT_SECRET: secret://ORDERS_API_JWT_SECRET
# Adios injects DATABASE_URL for the managed Postgres resource.
Terminal
(cd api && cp adios.release.yaml adios.yaml && adios up)
adios apps get orders-api

adios up builds, deploys and promotes a healthy release. It is a deployment command, not a local preview. Copy the actual HTTPS route from the output or Console and confirm that its /healthz returns 200.

Point MCP at the deployed API

Edit the manifest in your chosen language directory. Replace https://api.example.com with the API’s actual origin. Replace https://mcp.example.com with the MCP service’s public origin. These are placeholders, not domains supplied by Adios.

MCP environment settings
env:
  HOST: 0.0.0.0
  PORT: "8080"
  API_BASE_URL: https://YOUR-API-HOST
  PUBLIC_ORIGIN: https://YOUR-MCP-HOST
  API_JWT_SECRET: secret://ORDERS_API_JWT_SECRET
  MCP_JWT_SECRET: secret://ORDERS_MCP_JWT_SECRET

If you do not know the default MCP hostname yet, deploy once to obtain it, update PUBLIC_ORIGIN, then deploy again before connecting a client. Health probes work during this setup; the MCP host check accepts only its configured origin. Alternatively, verify a custom domain and add custom_hosts before the final release.

Optional — replace with your verified hostname
custom_hosts:
  - mcp.example.com

The MCP manifests bind to 0.0.0.0, expose port 8080 and use /healthz for health checks. Adios routes HTTPS to the app process. Keep the probe independent of MCP JWT authentication.

Run the deployment command from your selected implementation’s directory. Its subguide contains the exact command and manifest.

Open your language deployment steps →

Run the client check against the hosted URL:

Terminal
export MCP_ACCESS_TOKEN="$(node issue-token.mjs)"
export MCP_URL=https://YOUR-MCP-HOST/mcp
.client-venv/bin/python check.py

This walkthrough uses an HTTPS API route with an API-audience JWT. If you move the API behind private networking, use the hostname assigned by Adios and verify connectivity from the MCP runtime. Do not assume an internal hostname from the app name. See the manifest reference for runtime, secrets and routing options.

Use the hosted tools from an AI client

In a client supporting remote Streamable HTTP, add https://YOUR-MCP-HOST/mcp as the server URL. For the private examples, configure an Authorization: Bearer … header through the client’s supported secret or environment mechanism. Do not paste credentials into a shared configuration file.

For OAuth, add the same URL and complete the client’s sign-in flow. It should discover the resource metadata, open provider login and return with an access token. Confirm that the client supports your provider’s registration flow before distributing the integration.

Check that get_order appears in the tool list. Ask: “Use the Orders MCP server to check the shipping status of order demo-1001.” The client can call the tool and answer that it has shipped. Verify the tool call itself, rather than only an answer generated from chat context.

check.py demonstrates scripted initialization and invocation. With OAuth, it can accept a valid access token in MCP_ACCESS_TOKEN, but it does not perform login or token refresh; use an OAuth-capable client for that experience.

To let an AI client help deploy this project, connect it to Adios MCP. That platform connection and your hosted Orders MCP server are separate integrations with separate permissions.

Check the service before sharing it

The API health route checks PostgreSQL; the MCP health route checks its process. Neither proves that every JWT or OAuth permission is correct. After each release, test initialization, tool discovery and a known allowed tool call.

  • Connection fails: check the route, TLS, /mcp path and PUBLIC_ORIGIN. The proxy must preserve the intended host and support the selected transport.
  • 401 or 403: check credentials. For OAuth, verify issuer, audience, expiry, scopes and resource metadata. Test expired and wrong-audience tokens alongside a valid one.
  • Tool error: check the API URL, upstream permissions, timeout and response shape. Keep tokens and private response bodies out of logs.
  • Real users: add tenant and resource checks, request limits, credential rotation and audit events tied to the caller. Test that one user cannot read another user’s orders.
  • More replicas: these examples have no session state. If you add sessions, notifications or long-lived streams, plan shared state and routing.
Terminal
adios logs orders-api --runtime
adios logs orders-mcp --runtime
adios logs orders-mcp --build

Keep dependencies pinned and repeat the client check after changing API routes or authentication. Define permissions and confirmation behavior before adding write tools.

Common questions

Do I need OAuth to host an MCP server?

A controlled integration can use a short-lived, verified JWT if its client supports authorization headers. For user sign-in, delegated permissions or clients that require the MCP authorization flow, implement OAuth. Adios CLI login does not authenticate users of your own MCP server.

Can an MCP server wrap my existing API?

Yes. Define tools with input schemas, validate each request, call your API with a credential intended for that API, and return structured results. Enforce the caller’s tenant and resource permissions before accessing real data.

Can I host JavaScript, Python and Go MCP servers on Adios?

Yes. The linked JavaScript, Python and Go subguides each include a complete Streamable HTTP server, dependency files, health endpoints and an adios.yaml manifest. Choose one and deploy it alongside the shared REST API.

How is this different from Adios MCP?

This guide builds your own MCP service. Adios MCP lets an AI client operate Adios workspaces and deployments. You can use that platform connection to help build and deploy this project.

Explore other deployment guides