Class McpContextParams

java.lang.Object
com.mcpdbwizard.pub.McpContextParams

public final class McpContextParams extends Object
The rules for MCP context parameters: names declared in a config that a client supplies on the connection URL (/mcp/<owner>/<config>?CUSTOMER_ID=42), or under stdio as MCP_CONTEXT_<NAME> environment variables, and that the generated server places in the Oracle application context "MCP" for every tool call — so PL/SQL and curated SQL can read SYS_CONTEXT('MCP','CUSTOMER_ID').

What this protects against is the agent, not the person. The URL never enters the model's context and no tool can change it, so an agent cannot choose another customer. Anyone who can edit the client's configuration can — an accepted risk, and why a value here must never be a secret. See app/docs/mcp-per-caller-scoping-plan.md §0.

Lives in the runtime library, not the config model, because both ends need the same answer: the config editor refuses a name the generated server could never set, and the generated server refuses a value Oracle would reject. Copyright 2003-2026 ATB Consultancy Services Ltd (formerly Orinda Software Ltd, Dublin, Ireland)

  • Field Details

    • PB2_KEY_PREFIX

      public static final String PB2_KEY_PREFIX
      The config key family: MCP_CONTEXT_PARAM_<i>=<NAME>.
      See Also:
    • ENV_VARIABLE_PREFIX

      public static final String ENV_VARIABLE_PREFIX
      The stdio source: MCP_CONTEXT_<NAME>=<value>.
      See Also:
    • ORACLE_NAMESPACE

      public static final String ORACLE_NAMESPACE
      The Oracle application context namespace the values are set in.
      See Also:
    • ORACLE_PACKAGE

      public static final String ORACLE_PACKAGE
      The package app/db/mcp-context/install.sql creates, and the only code Oracle lets write the "MCP" namespace.
      See Also:
    • TRANSPORT_CONTEXT_KEY

      public static final String TRANSPORT_CONTEXT_KEY
      The key a generated server's HTTP transport files the parsed query string under, in the per-request McpTransportContext its tool handlers read back.
      See Also:
    • MAX_NAME_LENGTH

      public static final int MAX_NAME_LENGTH
      The longest name accepted. 30 is the DBMS_SESSION.SET_CONTEXT attribute limit on 12.1; later versions allow more, but a config that works on one box and not another is the failure this estate exists to catch, so the portable limit is the only limit.
      See Also:
    • MAX_VALUE_BYTES

      public static final int MAX_VALUE_BYTES
      The DBMS_SESSION.SET_CONTEXT value limit, in bytes.
      See Also:
  • Method Details

    • normalise

      public static String normalise(String theName)
      The canonical form of a name: trimmed and upper case. Null stays null.
      Parameters:
      theName - a name as typed or as it arrived on a URL
      Returns:
      the stored form
    • nameProblem

      public static String nameProblem(String theName)
      Why this name cannot be declared, or null when it can. Checked after normalise(java.lang.String), so lower case is accepted and stored upper.
      Parameters:
      theName - the name as typed
      Returns:
      a sentence naming the problem, or null
    • listProblem

      public static String listProblem(List<String> theNames)
      Why this list of names cannot be declared together, or null when it can: each name must be valid, and no name may appear twice (after normalising, so customer_id and CUSTOMER_ID are the same name).
      Parameters:
      theNames - the names in declaration order
      Returns:
      a sentence naming the first problem, or null
    • fromQueryString

      public static McpContextParams.Resolved fromQueryString(String theQuery, List<String> theDeclared)
      The values on one HTTP request's query string, checked against what the config declared.

      Refused, naming the parameter: a name not declared (a typo must not run with the context unset); a name given twice (which one was meant?); a declared name missing or empty (Oracle stores '' as NULL); a value over MAX_VALUE_BYTES. Names match case-insensitively. Read from the raw query string rather than getParameter, which may parse a request body and does not report a repeated name as a repeat.

      Parameters:
      theQuery - HttpServletRequest.getQueryString(), possibly null
      theDeclared - the names the config declares, normalised
      Returns:
      the values, or the reason to refuse
    • fromEnvironment

      public static McpContextParams.Resolved fromEnvironment(Map<String,String> theEnvironment, List<String> theDeclared)
      The values a stdio server takes from its environment: MCP_CONTEXT_<NAME> for each declared name. Fixed for the life of the process. Only declared names are looked up, so an unrelated variable that happens to share the prefix is not a refusal.
      Parameters:
      theEnvironment - usually System.getenv()
      theDeclared - the names the config declares, normalised
      Returns:
      the values, or the reason to refuse
    • applyToSession

      public static void applyToSession(Connection theConnection, Map<String,String> theValues) throws SQLException
      Replace this session's "MCP" context with exactly these values, in ONE round trip: clear everything, then set each pair.

      Clear first, every time, including when the map is empty. Connections are pooled, so a session that served one caller is handed to the next; clearing only on the way out would leak the previous caller's values whenever that path did not run (a timeout, a thread killed mid-call). Clearing at the start cannot be skipped by anything that happened before.

      Names and values are bound, never concatenated, and the package re-checks both, so a caller that skipped nameProblem(java.lang.String)/valueProblem(java.lang.String, java.lang.String) still cannot set an invalid name or a NULL.

      Parameters:
      theConnection - the session to set them on
      theValues - name to value, in the order to set them; names already normalised
      Throws:
      SQLException - when the package is missing or not granted (PLS-00201 inside an ORA-06550), or rejects a name or value (ORA-20901 / ORA-20902). The caller must not run the tool call on this session afterwards.
    • notInstalledReason

      public static String notInstalledReason(Throwable theFailure)
      What to tell an operator when applyToSession(java.sql.Connection, java.util.Map<java.lang.String, java.lang.String>) failed because the context is not there to set: the package missing, or this account not granted it. Null for any other failure, which should be reported as it stands.

      Takes any throwable and walks its causes, because a pooled server reaches the database through a wrapper that re-throws the driver's exception inside its own.

      Parameters:
      theFailure - what applyToSession(java.sql.Connection, java.util.Map<java.lang.String, java.lang.String>), or the borrow around it, threw
      Returns:
      the sentence to log, or null
    • valueProblem

      public static String valueProblem(String theName, String theValue)
      Why this value cannot be set, or null when it can. Missing and empty are both refused. Length is measured in UTF-8 bytes, because that is what Oracle's limit counts on an AL32UTF8 database; a value is refused rather than truncated, since a truncated customer id is a different customer.
      Parameters:
      theName - the parameter, for the message
      theValue - the value as supplied
      Returns:
      a sentence naming the problem, or null