MCPContext
The MCPContext class provides information about the current call of a tool of the MCP server, and lets the tool ask the user for a confirmation or for values before running an action (see Asking the user for confirmation).
History
| Release | Changes |
|---|---|
| 21 R6 | Class added |
MCPContext Object
MCPContext objects are returned by the MCP command.
The object is attached to the process while a custom tool function runs, and it is available in the methods and functions called by this function. Anywhere else (for example in a REST request or in a method run from the 4D environment), the object is inert: its functions do nothing and its properties are empty.
Summary
| .bound : any the bind value passed to .requireInput() when the input was asked, as verified by 4D |
| .canElicit : Boolean True if the MCP client can display input forms |
| .clientInfo : Object information about the MCP client that sent the request |
| .error( message : Text ) makes the current tool call return an error result with message |
| .hasInput( name : Text ) : Boolean returns True if the current call contains a verified answer of the user for the name field |
| .input : Object the verified answers of the user, one property per field requested with .requireInput() |
| .requireInput( fields : Object {; bind : any} ) : Object asks the user, through the MCP client, to fill in a form before the tool continues |
| .toolName : Text the name of the MCP tool being called |
.bound
.bound : any
Description
The .bound property contains the bind value passed to .requireInput() when the input was asked, as verified by 4D.
The value comes back signed with the user's answers, so the function can compare it with the current value and detect a change that occurred while the user was answering (for example, a modified entity). It is empty when the call does not carry verified answers.
The bind value is signed, not encrypted: the client can read it.
This property is read-only.
Example
You check that the entity was not modified while the user was confirming its deletion:
If (MCP.bound#This.getStamp())
MCP.error("The record was modified, please try again")
return
End if
.canElicit
.canElicit : Boolean
Description
The .canElicit property contains True if the MCP client can display input forms (it declared the elicitation capability in the request), and False otherwise or outside an MCP tool call.
When .canElicit is False, .requireInput() asks nothing and the call returns the error "This action requires a confirmation your client cannot display".
This property is read-only.
Example
You return a clear message when the client cannot display the confirmation:
If (Not(MCP.canElicit))
MCP.error("Please confirm this action from the application")
return
End if
.clientInfo
.clientInfo : Object
Description
The .clientInfo property contains information about the MCP client that sent the request, as provided in the metadata (_meta) of the request, for example its name and version. It is Null outside an MCP tool call.
This property is read-only.
Example
You log the client that called the tool:
var $client:=MCP.clientInfo
If ($client#Null)
LOG EVENT(Into 4D debug message; "MCP call from "+String($client.name))
End if
.error()
.error( message : Text )
| Parameter | Type | Description | |
|---|---|---|---|
| message | Text | → | Error message returned to the client |
Description
The .error() function makes the current tool call return an error result with message. The result has isError: true, and the value returned by the function is ignored.
.error() does not stop the execution of the function: call return after it if needed.
Outside an MCP tool call, .error() does nothing.
Example
You refuse an amount that is too high:
exposed MCP Function refund($amount : Real) : Text
If ($amount>1000)
MCP.error("Refunds over 1000 must be done by an accountant")
return
End if
// ...
.hasInput()
.hasInput( name : Text ) : Boolean
| Parameter | Type | Description | |
|---|---|---|---|
| name | Text | → | Name of a field requested with .requireInput() |
| Result | Boolean | ← | True if the user answered this field |
Description
The .hasInput() function returns True if the current call contains a verified answer of the user for the name field.
The answer is verified when the client calls the tool again with the answers and a valid requestState: signed by 4D for the same token, tool, arguments, and fields, less than 10 minutes ago, and since the MCP server was last enabled. Otherwise, .hasInput() returns False.
Outside an MCP tool call, .hasInput() returns False.
Example
You ask for a confirmation only if the user has not answered yet:
If (Not(MCP.hasInput("confirm")))
MCP.requireInput({confirm: {type: "boolean"; title: "Send the invoice to the customer"}})
return
End if
.input
.input : Object
Description
The .input property contains the verified answers of the user, one property per field requested with .requireInput(). Only answers whose type matches the type of the requested field are kept.
It does not contain any answer when the call does not carry verified answers, or outside an MCP tool call.
This property is read-only.
Example
You use the comment entered by the user:
If (MCP.hasInput("comment"))
This.closingComment:=MCP.input.comment
End if
.requireInput()
.requireInput( fields : Object {; bind : any} ) : Object
| Parameter | Type | Description | |
|---|---|---|---|
| fields | Object | → | Message and fields to display to the user |
| bind | any | → | Value signed with the answers, returned in .bound |
| Result | Object | ← | Null when the input is requested |
Description
The .requireInput() function asks the user, through the MCP client, to fill in a form before the tool continues.
In fields, pass an object with:
- an optional
messageproperty (Text): the text displayed to the user, - one property per field to display, whose name is the field name and whose value is the JSON Schema of a
"boolean","string","number", or"integer"field (for example{type: "boolean"; title: "Confirm"}). A field is required unless its schema containsrequired: False.
When the tool function returns, 4D answers the call with resultType: "input_required", the form, and a requestState signed by 4D. The client displays the form, then calls the tool again with the answers. The function then runs again from the top: .hasInput() returns True for the answered fields and .input contains the answers. If the answers are already there and bind has the same value as when the input was asked, .requireInput() does not ask again.
In bind, you can pass a value to bind to the answers, such as the stamp of an entity: it is signed with the answers and returned in .bound when the client calls the tool again, so that you can check that nothing changed in the meantime. If bind changed, the input is asked again.
If the client cannot display forms (see .canElicit), .requireInput() asks nothing and the call returns the error "This action requires a confirmation your client cannot display".
Outside an MCP tool call, .requireInput() asks nothing and returns Null.
Do not ask for secrets such as passwords with .requireInput(): the answers go through the AI client.
Example
You ask the user to confirm the closing of a ticket and to enter an optional comment:
// cs.TicketEntity class
Class extends Entity
exposed MCP Function close() : Text
If (Not(MCP.hasInput("confirm")))
MCP.requireInput({message: "Close ticket "+String(This.ID)+"?"; confirm: {type: "boolean"; title: "Close"}; comment: {type: "string"; title: "Comment"; required: False}}; This.getStamp())
return
End if
If (MCP.input.confirm)
This.status:="closed"
This.comment:=String(MCP.input.comment)
This.save()
End if
return This.status
.toolName
.toolName : Text
Description
The .toolName property contains the name of the MCP tool being called, for example "OrderManager-getYearTotal". It is an empty string outside an MCP tool call.
This property is read-only.
Example
You run a method only when it is called by an MCP tool:
If (MCP.toolName#"")
// called from an MCP tool
End if