Skip to content

Add McpPromptTrigger and McpPromptArgument annotations#235

Open
ahmedmuhsin wants to merge 2 commits intoAzure:devfrom
ahmedmuhsin:feature/mcp-prompt-annotations
Open

Add McpPromptTrigger and McpPromptArgument annotations#235
ahmedmuhsin wants to merge 2 commits intoAzure:devfrom
ahmedmuhsin:feature/mcp-prompt-annotations

Conversation

@ahmedmuhsin
Copy link
Copy Markdown
Contributor

@ahmedmuhsin ahmedmuhsin commented Apr 21, 2026

Summary

Adds annotation definitions for MCP prompt support (issue #861).

New annotations

McpPromptTrigger

Trigger annotation for MCP prompt functions. Properties:

  • name() — binding parameter name and unique prompt identifier (same convention as McpToolTrigger)
  • description() — human-readable description
  • title() — optional display title
  • promptArguments() — inline JSON array of argument definitions (alternative to McpPromptArgument annotations)
  • metadata() — optional JSON metadata
  • icons() — optional JSON icons array
  • dataType() — serialization hint

McpPromptArgument

Input binding annotation for individual prompt arguments. Properties:

  • name() — binding parameter name
  • argumentName() — argument name exposed in MCP protocol
  • description() — argument description
  • isRequired() — whether the argument is required

Usage

@FunctionName("CodeReviewPrompt")
public String codeReview(
    @McpPromptTrigger(name = "code_review", description = "Code review prompt", title = "Code Review")
    String context,
    @McpPromptArgument(name = "code", argumentName = "code", description = "The code to review", isRequired = true)
    String code,
    @McpPromptArgument(name = "language", argumentName = "language", description = "The programming language")
    String language
) {
    return "Please review the following " + language + " code:\n\n" + code;
}

Related PRs

Implements annotation definitions for MCP prompt support (issue #861).
- McpPromptTrigger: trigger annotation for prompt functions with name,
  description, title, promptArguments, metadata, and icons properties
- McpPromptArgument: input binding annotation for individual prompt
  arguments with argumentName, description, and isRequired properties

The name() property serves as both the binding parameter name and the
unique prompt identifier (same convention as McpToolTrigger).
Copy link
Copy Markdown
Collaborator

@TsuyoshiUshio TsuyoshiUshio left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: Add McpPromptTrigger and McpPromptArgument annotations

Thanks for this PR — it's well-structured and follows the existing McpToolTrigger/McpToolProperty pattern closely. The Javadoc quality is excellent. A few items to address before merge:


Block: Missing promptName field — host contract mismatch

The host extension contract (per issue #861) specifies separate fields in the binding JSON:

{
  "name": "<paramName>",
  "promptName": "<unique-prompt-name>"
}

The Python library implements this correctly with name and prompt_name as independent parameters:

class MCPPromptTrigger(Trigger):
    def __init__(self, name: str, prompt_name: str, ...):
        self.promptName = prompt_name

However, this Java PR merges both into a single name() method. Without a separate promptName field, the Maven plugin cannot emit the correct function.json structure unless it duplicates name() into both fields.

Question: Does the companion Maven plugin PR (#2554) handle this by copying name() into both name and promptName? If so, this works but should be documented in the Javadoc. If not, a promptName() attribute is needed.

Note: The same issue exists on McpToolTrigger (no toolName), so this may be an intentional Java-specific convention — but it diverges from both the Python SDK and the host contract spec.


Block: Dangling @see McpMetadata reference

McpPromptTrigger.java line 58 references @see McpMetadata, but no McpMetadata class exists in the codebase. This will produce a Javadoc warning/error at build time.

Fix: Either remove @see McpMetadata or introduce the McpMetadata class in this PR.


Concern: argumentName() pattern differs from McpToolProperty

McpPromptArgument introduces argumentName() as a separate field from name(), allowing the MCP protocol argument name to differ from the binding parameter name. McpToolProperty does not have an equivalent propertyName() accessor.

This is arguably an improvement (more flexible), but creates an asymmetry across the two MCP annotation families. Please confirm the Maven plugin companion PR handles the argumentName default (falls back to name() when empty).


Concern: No tests included

The Python library includes tests in test_mcp.py for their prompt trigger implementation. While the existing McpToolTrigger/McpToolProperty also lack tests in this repo, new public API (@since 3.3.0) would benefit from basic annotation contract tests — verify RUNTIME retention, PARAMETER target, default values, and that name() is required.


Suggestion: Consider adding title/metadata/icons to McpToolTrigger for parity

McpPromptTrigger has title(), metadata(), and icons() which are absent from McpToolTrigger. The MCP spec supports annotations/metadata for tools as well. If these will eventually be added to McpToolTrigger, consider doing it in a coordinated PR to keep the two annotation families consistent.


Nit: Example prompt name in McpPromptArgument Javadoc

In the McpPromptArgument example (line 26), the trigger uses name = "context". Since name doubles as the prompt identifier, "context" reads like a generic binding name rather than a prompt identifier. Using "code_review" would be clearer and consistent with the McpPromptTrigger summarize example.


Praise

  • Excellent Javadoc with two usage examples (inline JSON args vs annotation-based) — makes the API very learnable
  • Clean @see/@since cross-referencing
  • isRequired() default false is consistent with McpToolProperty
  • Follows the established McpToolTrigger/McpToolProperty pattern faithfully

* {@literal @}McpPromptTrigger(name = "context", description = "Code review prompt") String context,
* {@literal @}McpPromptArgument(
* name = "code",
* argumentName = "code",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is there both a name and argumentName property for McpPromptArgument?

The spec shows only name, description, and required properties:

[
  {
    "name": "code",
    "description": "The code to review",
    "required": true
  },
  {
    "name": "language",
    "description": "The programming language",
    "required": false
  }
]

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question. Removed argumentName() in the latest push. name() now serves as both the binding parameter name and the MCP argument identifier. The Maven plugin patches it into argumentName in function.json, same pattern as McpToolProperty where name() is patched into propertyName.

*
* @return The prompt name / parameter binding name
*/
String name();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is name different from promptName?
Per the spec from Lilian:

"name": "<paramName>",
"promptName": "<unique-prompt-name>",

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

name() serves as both the binding parameter name and the unique prompt identifier. The Maven plugin PR (#2554) patches it into promptName in function.json. This is the same convention as McpToolTrigger where name() is patched into toolName. Added Javadoc clarifying this.

- Remove argumentName() from McpPromptArgument: name() serves as both
  the binding parameter name and the MCP argument identifier, matching
  the McpToolProperty pattern where name() maps to propertyName
- Fix Javadoc example in McpPromptArgument: use code_review as prompt
  name instead of context
- Remove argumentName from McpPromptTrigger Javadoc example
- Add Javadoc notes explaining the name/argumentName patching convention
@ahmedmuhsin
Copy link
Copy Markdown
Contributor Author

ahmedmuhsin commented Apr 21, 2026

@TsuyoshiUshio

Thanks for the thorough review! Addressed all items in the latest push:

1. Missing promptName field:
Good catch. The Maven plugin PR (#2554) handles this by patching name() into both the name (binding parameter) and promptName fields in function.json, same pattern as McpToolTrigger where name() is patched into toolName. I've added Javadoc on name() documenting this convention.

2. Dangling @see McpMetadata:
McpMetadata was added in 3.2.4 and is in the dev branch. It may not have been visible if reviewing against an older base.

3. argumentName() asymmetry:
Agreed, removed it. name() now serves as both the binding name and the MCP argument identifier, matching the McpToolProperty pattern exactly. The Maven plugin patches name into argumentName in function.json. Updated in latest push.

4. No tests:
Annotation-level tests aren't the pattern for this repo. Integration tests for prompt support will be added to the worker repo.

5. Add title/metadata/icons to McpToolTrigger:
The C# host extension's McpToolTriggerAttribute doesn't support title or icons for tools either. Tool metadata is already handled via the separate @McpMetadata annotation. This is out of scope for this PR.

6. Example uses "context":
Fixed in the latest push. Examples now use "code_review" as the prompt name.

*
* @return JSON array of argument definitions, or empty string
*/
String promptArguments() default "";
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of string, can you have more fluent api to define the prompt arguments?

Current implementation require customer to manage the JSON string.

Copy link
Copy Markdown
Collaborator

@TsuyoshiUshio TsuyoshiUshio left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants