{
  "manifest_version": "1.0.0",
  "template": {
    "id": "b2e76c38-e1ad-4d49-869f-cc082e5ea924",
    "slug": "bexio-mcp",
    "name": "Bexio MCP",
    "description": "Bexio MCP server with token-protected HTTP access to your business data.",
    "url": "https://railway.com/deploy/bexio-mcp",
    "upstream": {
      "repo_url": "https://github.com/PromptPartner/bexio-mcp-server"
    }
  },
  "status": "unvalidated",
  "validated_at": null,
  "success_rate_30d": null,
  "services": [
    {
      "name": "bexio-mcp",
      "source": {
        "repo": "https://github.com/PromptPartner/bexio-mcp-server"
      },
      "needs_volume": true,
      "volume_mount_path": "/data",
      "tcp_ports": [
        8080
      ],
      "http": true
    }
  ],
  "required_inputs": [
    {
      "key": "PORT",
      "service": "bexio-mcp",
      "description": "Preconfigured HTTP listening port. The start command validates and passes this value to the server. Configure public networking with the same target port.",
      "secret": false,
      "strategy": "default",
      "default": "8080"
    },
    {
      "key": "TMPDIR",
      "service": "bexio-mcp",
      "description": "Temporary directory available during the build. At runtime, the start command changes TMPDIR to BEXIO_FILE_DIR/tmp after the volume is mounted. Stored downloads are not automatically deleted.",
      "secret": false,
      "strategy": "default",
      "default": "/tmp"
    },
    {
      "key": "NODE_ENV",
      "service": "bexio-mcp",
      "description": "Production runtime mode. Required build dependencies are explicitly installed by RAILPACK_INSTALL_CMD.",
      "secret": false,
      "strategy": "default",
      "default": "production"
    },
    {
      "key": "BEXIO_BASE_URL",
      "service": "bexio-mcp",
      "description": "Bexio API base URL. Keep the default for normal use. To restore the application default, delete this variable entirely; an explicitly empty value does not restore the default.",
      "secret": false,
      "strategy": "default",
      "default": "https://api.bexio.com/2.0"
    },
    {
      "key": "BEXIO_FILE_DIR",
      "service": "bexio-mcp",
      "description": "Automatically uses the attached persistent volume for server-side files. Requires a volume mounted at /data. Startup creates /data/files and /data/files/tmp with restricted permissions. Local file paths refer to the server, not the client computer.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "BEXIO_API_TOKEN",
      "service": "bexio-mcp",
      "description": "Required manual input. Enter your Bexio Personal Access Token from https://developer.bexio.com. The token must have the permissions needed for your workflows.",
      "secret": true,
      "strategy": "generate",
      "generate": "random_base64_32"
    },
    {
      "key": "BEXIO_ENABLE_UI",
      "service": "bexio-mcp",
      "description": "Interactive panels are available only in stdio mode. Keep false for this HTTP deployment. This setting does not enable panels on the HTTP endpoint.",
      "secret": false,
      "strategy": "default",
      "default": "false"
    },
    {
      "key": "BEXIO_HTTP_TOKEN",
      "service": "bexio-mcp",
      "description": "Automatically generated authentication token for this server. Clients must send Authorization: Bearer <this value>. Separate from your Bexio API token. No manual input needed.",
      "secret": true,
      "strategy": "generate",
      "generate": "random_base64_32"
    },
    {
      "key": "RAILPACK_BUILD_CMD",
      "service": "bexio-mcp",
      "description": "Build the TypeScript server and bundled UI assets. The persistent volume is not available during the build.",
      "secret": false,
      "strategy": "default",
      "default": "npm run build"
    },
    {
      "key": "RAILPACK_START_CMD",
      "service": "bexio-mcp",
      "description": "Check required token values, port, timezone, volume and file permissions before starting. Preserve a configured TZ or default to Europe/Zurich. Use the volume for runtime temporary files and start HTTP on IPv4/IPv6. Keep this command unchanged; npm start defaults to stdio.",
      "secret": false,
      "strategy": "default",
      "default": "/bin/sh -ec '#!/bin/sh\nset -eu\numask 077\nexport TZ=\"${TZ:-Europe/Zurich}\"\nnode --input-type=commonjs <<'\"'\"'BEXIO_PREFLIGHT'\"'\"'\nconst fs=require('\"'\"'node:fs'\"'\"'),p=require('\"'\"'node:path'\"'\"'),e=process.env;\nconst fail=m=>{console.error('\"'\"'Configuration error: '\"'\"'+m);process.exit(1)};\ntry{new Intl.DateTimeFormat('\"'\"'en'\"'\"',{timeZone:e.TZ}).format()}catch{fail('\"'\"'TZ must be a valid IANA timezone, for example Europe/Zurich'\"'\"')}\nfor(const k of ['\"'\"'BEXIO_API_TOKEN'\"'\"','\"'\"'BEXIO_HTTP_TOKEN'\"'\"'])if(!e[k]?.trim()||e[k].includes('\"'\"'$'\"'\"'+'\"'\"'{{'\"'\"'))fail(k+'\"'\"' is required and must be resolved'\"'\"');\nif(!/^[0-9]+$/.test(e.PORT||'\"'\"''\"'\"')||+e.PORT<1||+e.PORT>65535)fail('\"'\"'PORT must be 1..65535'\"'\"');\nfor(const k of ['\"'\"'RAILWAY_VOLUME_MOUNT_PATH'\"'\"','\"'\"'BEXIO_FILE_DIR'\"'\"']){const v=e[k];if(!v||v!==v.trim()||/[\\x00-\\x1f]/.test(v)||!p.isAbsolute(v)||p.resolve(v)===p.parse(v).root)fail(k+'\"'\"' must be an absolute non-root directory'\"'\"')}\nif(!fs.existsSync(e.RAILWAY_VOLUME_MOUNT_PATH)||!fs.statSync(e.RAILWAY_VOLUME_MOUNT_PATH).isDirectory())fail('\"'\"'Volume directory must already exist'\"'\"');\nconst inside=(a,b)=>{const r=p.relative(a,b);return r!=='\"'\"''\"'\"'&&r!=='\"'\"'..'\"'\"'&&!r.startsWith('\"'\"'../'\"'\"')&&!p.isAbsolute(r)},mount=fs.realpathSync(e.RAILWAY_VOLUME_MOUNT_PATH);\nif(!inside(p.resolve(e.RAILWAY_VOLUME_MOUNT_PATH),p.resolve(e.BEXIO_FILE_DIR)))fail('\"'\"'BEXIO_FILE_DIR must be inside the volume'\"'\"');\nfs.mkdirSync(e.BEXIO_FILE_DIR,{recursive:true,mode:448});const base=fs.realpathSync(e.BEXIO_FILE_DIR);if(!inside(mount,base))fail('\"'\"'File directory resolves outside the volume'\"'\"');\nconst tmp=p.join(base,'\"'\"'tmp'\"'\"');fs.mkdirSync(tmp,{recursive:true,mode:448});if(!inside(base,fs.realpathSync(tmp)))fail('\"'\"'Temp directory resolves outside the file directory'\"'\"');\nfor(const d of [base,tmp]){fs.chmodSync(d,448);const probe=fs.mkdtempSync(p.join(d,'\"'\"'.write-test-'\"'\"'));fs.rmdirSync(probe)}\nBEXIO_PREFLIGHT\nexport TMPDIR=\"$BEXIO_FILE_DIR/tmp\"\nexec node dist/index.js --mode http --host :: --port \"$PORT\"'"
    },
    {
      "key": "RAILPACK_INSTALL_CMD",
      "service": "bexio-mcp",
      "description": "Install dependencies from package-lock.json, including TypeScript and UI build tools. Requires the service Root Directory to be /src.",
      "secret": false,
      "strategy": "default",
      "default": "npm ci --include=dev"
    },
    {
      "key": "BEXIO_DEFAULT_COMPANY",
      "service": "bexio-mcp",
      "description": "Internal label for the default company. Does not rename your company in Bexio. For multiple company tokens, use a matching company label.",
      "secret": false,
      "strategy": "default",
      "default": "default"
    },
    {
      "key": "RAILPACK_NODE_VERSION",
      "service": "bexio-mcp",
      "description": "Use the latest eligible Node.js LTS release resolved by Railpack at build time. Future builds may upgrade to a newer LTS major version. Existing deployments require a rebuild to receive updates.",
      "secret": false,
      "strategy": "default",
      "default": "lts"
    },
    {
      "key": "BEXIO_DOWNLOAD_INLINE_MAX_BYTES",
      "service": "bexio-mcp",
      "description": "Maximum decoded file size returned inline as base64. Larger downloads are saved on the server. This does not increase the upstream 1 MiB HTTP request limit. Keep 64000 for the default behavior; an empty value is interpreted as 0. Saved file paths are not public download links.",
      "secret": false,
      "strategy": "default",
      "default": "64000"
    }
  ],
  "deploy": {
    "mcp": {
      "server": "railway",
      "tool": "deploy_template",
      "args": {
        "template_code": "bexio-mcp"
      }
    },
    "cli": "railway deploy --template bexio-mcp",
    "api": {
      "method": "POST",
      "path": "/graphql/v2",
      "body": {
        "query": "mutation templateDeploy($input: TemplateDeployV2Input!) { templateDeployV2(input: $input) { projectId workflowId } }",
        "variables": {
          "input": {
            "templateId": "b2e76c38-e1ad-4d49-869f-cc082e5ea924",
            "serializedConfig": {
              "services": {
                "ffcfe3bb-32ef-4fc5-ba60-77dd28becfd7": {
                  "name": "bexio-mcp",
                  "deploy": {
                    "healthcheckPath": "/"
                  },
                  "source": {
                    "repo": "PromptPartner/bexio-mcp-server",
                    "branch": "master",
                    "rootDirectory": "/src"
                  },
                  "variables": {
                    "TZ": {
                      "isOptional": true,
                      "description": "Optional. Leave empty to use Europe/Zurich at runtime, including daylight-saving changes. A configured timezone takes precedence. To use an existing shared project variable named TZ, enter ${{shared.TZ}}. Use an IANA timezone name. Application logger timestamps remain ISO UTC.",
                      "defaultValue": ""
                    },
                    "PORT": {
                      "description": "Preconfigured HTTP listening port. The start command validates and passes this value to the server. Configure public networking with the same target port.",
                      "defaultValue": "8080"
                    },
                    "TMPDIR": {
                      "description": "Temporary directory available during the build. At runtime, the start command changes TMPDIR to BEXIO_FILE_DIR/tmp after the volume is mounted. Stored downloads are not automatically deleted.",
                      "defaultValue": "/tmp"
                    },
                    "NODE_ENV": {
                      "description": "Production runtime mode. Required build dependencies are explicitly installed by RAILPACK_INSTALL_CMD.",
                      "defaultValue": "production"
                    },
                    "BEXIO_BASE_URL": {
                      "description": "Bexio API base URL. Keep the default for normal use. To restore the application default, delete this variable entirely; an explicitly empty value does not restore the default.",
                      "defaultValue": "https://api.bexio.com/2.0"
                    },
                    "BEXIO_FILE_DIR": {
                      "description": "Automatically uses the attached persistent volume for server-side files. Requires a volume mounted at /data. Startup creates /data/files and /data/files/tmp with restricted permissions. Local file paths refer to the server, not the client computer.",
                      "defaultValue": "${{RAILWAY_VOLUME_MOUNT_PATH}}/files"
                    },
                    "BEXIO_API_TOKEN": {
                      "description": "Required manual input. Enter your Bexio Personal Access Token from https://developer.bexio.com. The token must have the permissions needed for your workflows.",
                      "defaultValue": "{{BEXIO_API_TOKEN}}"
                    },
                    "BEXIO_ENABLE_UI": {
                      "description": "Interactive panels are available only in stdio mode. Keep false for this HTTP deployment. This setting does not enable panels on the HTTP endpoint.",
                      "defaultValue": "false"
                    },
                    "BEXIO_API_TOKENS": {
                      "isOptional": true,
                      "description": "Optional additional company tokens. Leave empty for a single company. Accepts CompanyA:tokenA,CompanyB:tokenB or a JSON object. The active company is shared across HTTP requests; use separate services for concurrent company isolation.",
                      "defaultValue": ""
                    },
                    "BEXIO_HTTP_TOKEN": {
                      "description": "Automatically generated authentication token for this server. Clients must send Authorization: Bearer <this value>. Separate from your Bexio API token. No manual input needed.",
                      "defaultValue": "{{BEXIO_HTTP_TOKEN}}"
                    },
                    "RAILPACK_BUILD_CMD": {
                      "description": "Build the TypeScript server and bundled UI assets. The persistent volume is not available during the build.",
                      "defaultValue": "npm run build"
                    },
                    "RAILPACK_START_CMD": {
                      "description": "Check required token values, port, timezone, volume and file permissions before starting. Preserve a configured TZ or default to Europe/Zurich. Use the volume for runtime temporary files and start HTTP on IPv4/IPv6. Keep this command unchanged; npm start defaults to stdio.",
                      "defaultValue": "/bin/sh -ec '#!/bin/sh\nset -eu\numask 077\nexport TZ=\"${TZ:-Europe/Zurich}\"\nnode --input-type=commonjs <<'\"'\"'BEXIO_PREFLIGHT'\"'\"'\nconst fs=require('\"'\"'node:fs'\"'\"'),p=require('\"'\"'node:path'\"'\"'),e=process.env;\nconst fail=m=>{console.error('\"'\"'Configuration error: '\"'\"'+m);process.exit(1)};\ntry{new Intl.DateTimeFormat('\"'\"'en'\"'\"',{timeZone:e.TZ}).format()}catch{fail('\"'\"'TZ must be a valid IANA timezone, for example Europe/Zurich'\"'\"')}\nfor(const k of ['\"'\"'BEXIO_API_TOKEN'\"'\"','\"'\"'BEXIO_HTTP_TOKEN'\"'\"'])if(!e[k]?.trim()||e[k].includes('\"'\"'$'\"'\"'+'\"'\"'{{'\"'\"'))fail(k+'\"'\"' is required and must be resolved'\"'\"');\nif(!/^[0-9]+$/.test(e.PORT||'\"'\"''\"'\"')||+e.PORT<1||+e.PORT>65535)fail('\"'\"'PORT must be 1..65535'\"'\"');\nfor(const k of ['\"'\"'RAILWAY_VOLUME_MOUNT_PATH'\"'\"','\"'\"'BEXIO_FILE_DIR'\"'\"']){const v=e[k];if(!v||v!==v.trim()||/[\\x00-\\x1f]/.test(v)||!p.isAbsolute(v)||p.resolve(v)===p.parse(v).root)fail(k+'\"'\"' must be an absolute non-root directory'\"'\"')}\nif(!fs.existsSync(e.RAILWAY_VOLUME_MOUNT_PATH)||!fs.statSync(e.RAILWAY_VOLUME_MOUNT_PATH).isDirectory())fail('\"'\"'Volume directory must already exist'\"'\"');\nconst inside=(a,b)=>{const r=p.relative(a,b);return r!=='\"'\"''\"'\"'&&r!=='\"'\"'..'\"'\"'&&!r.startsWith('\"'\"'../'\"'\"')&&!p.isAbsolute(r)},mount=fs.realpathSync(e.RAILWAY_VOLUME_MOUNT_PATH);\nif(!inside(p.resolve(e.RAILWAY_VOLUME_MOUNT_PATH),p.resolve(e.BEXIO_FILE_DIR)))fail('\"'\"'BEXIO_FILE_DIR must be inside the volume'\"'\"');\nfs.mkdirSync(e.BEXIO_FILE_DIR,{recursive:true,mode:448});const base=fs.realpathSync(e.BEXIO_FILE_DIR);if(!inside(mount,base))fail('\"'\"'File directory resolves outside the volume'\"'\"');\nconst tmp=p.join(base,'\"'\"'tmp'\"'\"');fs.mkdirSync(tmp,{recursive:true,mode:448});if(!inside(base,fs.realpathSync(tmp)))fail('\"'\"'Temp directory resolves outside the file directory'\"'\"');\nfor(const d of [base,tmp]){fs.chmodSync(d,448);const probe=fs.mkdtempSync(p.join(d,'\"'\"'.write-test-'\"'\"'));fs.rmdirSync(probe)}\nBEXIO_PREFLIGHT\nexport TMPDIR=\"$BEXIO_FILE_DIR/tmp\"\nexec node dist/index.js --mode http --host :: --port \"$PORT\"'"
                    },
                    "RAILPACK_INSTALL_CMD": {
                      "description": "Install dependencies from package-lock.json, including TypeScript and UI build tools. Requires the service Root Directory to be /src.",
                      "defaultValue": "npm ci --include=dev"
                    },
                    "BEXIO_DEFAULT_COMPANY": {
                      "description": "Internal label for the default company. Does not rename your company in Bexio. For multiple company tokens, use a matching company label.",
                      "defaultValue": "default"
                    },
                    "RAILPACK_NODE_VERSION": {
                      "description": "Use the latest eligible Node.js LTS release resolved by Railpack at build time. Future builds may upgrade to a newer LTS major version. Existing deployments require a rebuild to receive updates.",
                      "defaultValue": "lts"
                    },
                    "BEXIO_ENABLED_CATEGORIES": {
                      "isOptional": true,
                      "description": "Optional. Leave empty to enable all tool categories. To select categories, use comma-separated names: reference,company,banking,projects,timetracking,accounting,purchase,files,payroll,contacts,invoices,orders,quotes,payments,reminders,deliveries,items,reports,users,misc,notes,tasks,stock,docs,positions.",
                      "defaultValue": ""
                    },
                    "BEXIO_DOWNLOAD_INLINE_MAX_BYTES": {
                      "description": "Maximum decoded file size returned inline as base64. Larger downloads are saved on the server. This does not increase the upstream 1 MiB HTTP request limit. Keep 64000 for the default behavior; an empty value is interpreted as 0. Saved file paths are not public download links.",
                      "defaultValue": "64000"
                    }
                  },
                  "networking": {
                    "tcpProxies": {
                      "8080": {}
                    },
                    "serviceDomains": {
                      "<hasDomain>:8080": {
                        "port": 8080
                      }
                    }
                  },
                  "volumeMounts": {
                    "ffcfe3bb-32ef-4fc5-ba60-77dd28becfd7": {
                      "mountPath": "/data",
                      "backupSchedules": [
                        "DAILY",
                        "WEEKLY",
                        "MONTHLY"
                      ]
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "post_deploy": {
    "healthcheck": {
      "service": "bexio-mcp",
      "method": "GET",
      "path": "/",
      "expect_status": 200
    }
  },
  "resources": {
    "expected_services": 1,
    "needs_volume": true
  },
  "generated_at": "2026-10-08T16:14:40.577Z",
  "generator_version": "0.1.0"
}
