Skip to content

Reflect the latest registry changes in openapi.yaml - #345

Merged
domdomegg merged 7 commits into
modelcontextprotocol:mainfrom
rdimitrov:bump-openapi
Sep 5, 2025
Merged

domdomegg merged 7 commits into
modelcontextprotocol:mainfrom
rdimitrov:bump-openapi

Conversation

@rdimitrov

@rdimitrov rdimitrov commented Sep 2, 2025 •

Copy link
Copy Markdown
Member

Motivation and Context

The following PR:

  • Adds a new transport type used in Package and Remotes
  • Added docker to the runtime_hint examples
  • Added support for templated URLs. It is available for streamable-http and sse transports for Packages. I didn't thought of reasons to have this for Remotes, but we can enable it if needed.
  • Added validation for templated URLs so we can ensure all referenced placeholder variables are available.
  • For streamable-http and SSE transport types the URL is now a required property
  • Updated the publisher init command to reflect these changes
  • Updated the seed.json file
  • Added unit tests for the transport type related changes
  • Updated the server.json schema and the external pkg structs to reflect the new transport type
  • Updated the examples

Thanks to @kkkarthik for spotting it! 🙏

How Has This Been Tested?

Ran the tests locally

Breaking Changes

Yes

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

Additional context

@rdimitrov rdimitrov mentioned this pull request Sep 2, 2025
1 of 9 tasks
Comment thread docs/server-registry-api/openapi.yaml Outdated
$ref: '#/components/schemas/KeyValueInput'
transport_type:
type: string
enum: [ streamable-http, sse, stdio ]

@connor4312 connor4312 Sep 2, 2025 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This isn't enough for a client to know how to consume the package -- http/sse packages will need to expose their URL too, perhaps through templated input that can reference their args/environment variables?

@rdimitrov rdimitrov Sep 2, 2025 •

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I think that matches my assumption too. So far I've seen SSE/streamable-http servers typically allow you to set the port as an arg/env var (usually defaults to 8080) while the host usually comes from the runtime. And of course the rest of the path portion should follow the spec (e.g. /mcp, /sse, etc.).

I found this to be enough for clients to construct the URL, but I don't mind if you think we should have something more explicit, i.e. an additional templated property 👍

edit: The page refreshed right after I posted this and now I saw your next comment

@connor4312 connor4312 Sep 2, 2025 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Assuming the default port and path might work for a toy client but it's not something that works for complex scenarios where a client manages multiple MCP servers concurrently for example.

Maybe for the template we do something like this:

# in the package -- allow including extra data within the transport type
        transport_type:
          anyOf:
            - $ref: '#/components/schemas/StdioTransport'
            - $ref: '#/components/schemas/HttpTransport'
# stdio definition is pretty easy/static
    StdioTransport:
      type: object
      properties:
        type:
          type: string
          enum: [stdio]
      required:
        - type
# http transport type is more involved. While this is similar to http packages,
# I don't think there's a reason to do input with headers for example, so there's no reuse
    HttpTransport:
      type: object
      properties:
        type:
          type: string
          enum: [http]
        url:
          type: string
          example: http://{host}:{port}/my-server/mcp
          description: |
            Fully qualified URL for the streamable HTTP when the server is started. Clients should retry connecting to this URL as it may take some time for the server to be ready.

            Identifiers wrapped in `{curly_braces}` should be looked up and replaced with the corresponding value of the input, based on the `value_hint` in PositionalArguments or the `name` in NamedArguments or KeyValueInput. The input's `default` value should be used if the input was not explicitly configured.
        headers:
          type: array
          items:
            - type: object
              required:
                - name
                - value
              properties:
                name:
                  type: string
                  description: Name of the header.
                  example: SOME_VARIABLE
                value:
                  type: string
                  description: Value of the header. Identifiers may be wrapped in `{curly_braces}` to reference existing variables, following the same rules at HttpTransport.url.
      required:
        - type
        - url

This would let it work to specify a URL or defaults based on the server's spawn arguments. The curly_braces format is reused from templated arguments, although the reference mechanic is new. This also works to do nonces (as I suggest below) if we add some tag like is_randomized on an argument.

SSE would basically be the same, but as SSE was removed from the spec back in June I'm not convinced we need to support it in new work going forward :)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yeah, I see your point now, I agree it's better to make this deterministic for clients 👍 I'll proceed with incorporating your feedback into this PR so we can keep the discussion here 👍 I'll probably address the URL part now and file an issue for the nonce part so we can do it in a follow up.

Thanks for your detailed replies, it is much appreciated! 🙏

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@connor4312 - hey, whenever you get a chance could you take another look? I've updated the PR description too to better match the changes.

@connor4312

Copy link
Copy Markdown
Contributor

Continuing from #340 (comment)

I think it's not necessary to be advertised via remotes, usually it's on host-address:8080 (depending on the server there can be a flag/env var to set the port it listens on) and the host-address bit depends on your runtime. From there on if you say you support/want to talk over sse there should be an /sse endpoint and similarly for streamable there's /message I believe (someone correct me if I got it wrong, haven't checked it in detail)

It is definitely necessary. Ports are not standardized and of course only one process can listen on a port. Nor are particular subpaths, such as /sse or /message, standardized in the spec. For a client to consume an http/sse package there must be a URL or algorithm to derive the URL specified in the package's metadata on the registry.

We should also think about how we can ensure HTTP MCP servers running locally can be connected to securely. A common attack vector is escalation of privledge from services or users running on the same machine or network. Best practice for this kind of thing is to include a randomized nonce (be it a header, query string, or URL fragment -- a header is easiest for MCP) that the client and server share. So I suggest a means to include that as well when thinking about how the URL gets specified.

@rdimitrov
rdimitrov force-pushed the bump-openapi branch 2 times, most recently from 214216a to d652b59 Compare September 2, 2025 23:44
Comment thread docs/server-registry-api/openapi.yaml Outdated
example: "streamable-http"
url:
type: string
format: uri

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This technically might not be a uri format itself. After interpolation it should produce a URI, but the template string might not be a URI before that. E.g. it could just be {uri} if I have a server that takes a named argument --listen-on {uri}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Thanks! I'll update that on both schemas since the server.json has it too 👍

@connor4312

Copy link
Copy Markdown
Contributor

API shape overall lgtm, will let others weigh in too :)

@tadasant

tadasant commented Sep 3, 2025

Copy link
Copy Markdown
Member

Did I miss a discussion somewhere about removing SSE? I'm not super strongly opposed, but I would guess something like 20-40% of remote servers in use in the wild today are still SSE. So it's a pretty significant move to not allow them in the registry.

I totally agree with removing them for local (not a common use case, has security issues), but would have thought to keep them in remotes

@tadasant tadasant left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for updating! Added some questions / suggestions.

Thanks for catching the bit about needing a URL for local streamable @connor4312 and @domdomegg, it's a good point. I would also not be opposed to just nixing HTTP-based local servers for now to cull down on our complexity here; I haven't seen a practical use case for that mode. Easy to just make the enum only allow stdio for now and add it later if folks present use cases.

Comment thread docs/server-json/server.schema.json Outdated
@@ -118,14 +118,15 @@
]
},
"transport_type": {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This key feels out of place IMO -- can we just flatten it? i.e. instead of creating remote.transport_type.type and remote.transport_type.url, etc, we just have remote.transport_type, remote.url, etc.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

i.e. positioning as "remotes is an array of Transport, which is one of StreamableHttpTransport (or SSETransport, if we choose to re-add that; and perhaps soon WebSocketTransport pending that ongoing SEP)"

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This key feels out of place IMO -- can we just flatten it? i.e. instead of creating remote.transport_type.type and remote.transport_type.url, etc, we just have remote.transport_type, remote.url, etc.

I tried this initially, but it turned out to be messier in practice. Having a dedicated transport object for each option ended up being clearer as schema validation got tricky, i.e. there wasn’t really an easy way to enforce which fields should be present for a given transport type.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I guess if we rename transport_type to just transport, this feels a lot better. Still a little odd that the only key in remote is transport, but maybe we'll come up with something in the future that needs to live on a Remote alongside transport that will make us happy we added the layer :)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I tried this initially, but it turned out to be messier in practice. Having a dedicated transport object for each option ended up being clearer as schema validation got tricky, i.e. there wasn’t really an easy way to enforce which fields should be present for a given transport type.

I'm not sure I follow this - are you able to expand more on this difficulty? I think I agree with Tadas that remotes should just be an array of transports I think.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I'm sorry, I see what you mean now 🤦

For some reason I read that as "let's keep up the old approach of having these properties under the remote object" instead of "replace the remote object with a list of transports".

Yes, your suggestion makes total sense! I'll implement it 🙏

Comment thread docs/server-json/examples.md Outdated
"registry_base_url": "https://docker.io",
"identifier": "mcp/filesystem",
"version": "1.0.2",
"transport_type": {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Seems like this would be better named:

Suggested change
"transport_type": {
"transport": {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yeah, I prefer transport too, I'll be happy to rename it 👍

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Actually would it be okay if I address that in a follow-up? I think it’ll be easier to review separately and I don’t want to make this PR too heavy. I can jump on it right after this one’s merged so it shouldn’t cause any extra friction

"format": "uri",
"description": "Remote server URL",
"example": "https://mcp-fs.example.com/sse"
"description": "URL template for the streamable-http transport. Variables in {curly_braces} reference argument value_hints, argument names, or environment variable names. After variable substitution, this should produce a valid URI.",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It seems a little odd to me to reach across other fields (value_hints, argument names, env vars) to fill out this url template. Should we consider keeping this consistent and just allowing for variable definitions adjacent to and/or inside url via KeyValueInput?

@connor4312

Copy link
Copy Markdown
Contributor

HTTP-based local servers for now to cull down on our complexity here

I think this is something that will be wanted eventually. HTTP servers can do extra things like OAuth (and form-based elicitations if that arrives in MCP) and we have an open feature request on VS Code to easily support spawnable MCP servers that use the HTTP transport.

@rdimitrov

rdimitrov commented Sep 3, 2025 •

Copy link
Copy Markdown
Member Author

Did I miss a discussion somewhere about removing SSE? I'm not super strongly opposed, but I would guess something like 20-40% of remote servers in use in the wild today are still SSE. So it's a pretty significant move to not allow them in the registry.

I totally agree with removing them for local (not a common use case, has security issues), but would have thought to keep them in remotes

You're right, we didn't 👍 I did it based on @connor4312's feedback as I agree it feels right to start the registry by being compliant with the spec regarding this. That said I can easily revert it if we agree this might affect adoption and we want to support the deprecated SSE (at least for now).

HTTP-based local servers for now to cull down on our complexity here

Personally I think we should have this. Especially if we want to allow for enterprise adoption besides the desktop experience of running MCP servers, i.e. it would be better to run a streamable server instead of stdio.

@tadasant

tadasant commented Sep 4, 2025

Copy link
Copy Markdown
Member

I think this is something that will be wanted eventually. HTTP servers can do extra things like OAuth (and form-based elicitations if that arrives in MCP) and we have an open feature request on VS Code to easily support spawnable MCP servers that use the HTTP transport.

Personally I think we should have this. Especially if we want to allow for enterprise adoption besides the desktop experience of running MCP servers, i.e. it would be better to run a streamable server instead of stdio.

Got it, fair enough, I'll retract my thought of potentially culling them then; let's keep it.


You're right, we didn't 👍 I did it based on @connor4312's feedback as I agree it feels right to start the registry by being compliant with the spec regarding this. That said I can easily revert it if we agree this might affect adoption and we want to support the deprecated SSE (at least for now).

We haven't yet been wielding the registry as a tool for pushing adoption of better practices (for example, we had a very brief idea like "maybe we should require all servers to have a docker image to standardize around that" way back that we shuttered), so I'm inclined to stick to that for now and optimize for registry adoption rather than try to thread a needle with this go live of easy-enough-to-adopt-but-still-pushing-better-practices. Probably should have added "meet the industry where it's currently at" as a design principle. Otherwise it's a slippery slope with making harder decisions like not allowing API Key headers and forcing all auth to be OAuth based. So I think we should keep SSE, at least in Remote, unless folks have strong objections.

@domdomegg domdomegg mentioned this pull request Sep 4, 2025
1 of 9 tasks
@domdomegg

domdomegg commented Sep 4, 2025 •

Copy link
Copy Markdown
Member

A few thoughts:

  • I think I'd prefer transport to transport_type, if we're going to have type and url underneath it
  • As much as I am for the death of SSE servers, I think we should prioritize supporting a wide array of servers which will have to include sse initially, and we should use other mechanisms to encourage the move to streamable http
  • Reaching over other fields does feel a bit weird, but is maybe okay. I think this is likely to be rare anyways.
  • A case I worry about is servers that try to spin up on a random port, no idea how to handle these. I think a reasonable answer to these is 'don't' for now... (appreciate this goes against my 'support wide range of things' above but I think this is much rarer than SSE)

@connor4312

connor4312 commented Sep 4, 2025 •

Copy link
Copy Markdown
Contributor

think a reasonable answer to these is 'don't' for now...

Yea, this is kind of nasty. In VS Code we have a 'serverReadyAction' in debug land that is essentially a regex on the process' stdout, but using it is painful and there has not been ideas to improve it in the last decade :) I think it's reasonable for HTTP servers to have some optional argument for a port, a way to reference that in the URL (as in this PR) and then users can change that port if it conflicts with something on their device. Smart clients might even see a port parameter and automatically default to an unused port on the machine without user intervention.

@rdimitrov

rdimitrov commented Sep 4, 2025 •

Copy link
Copy Markdown
Member Author

Thank you all for reviewing and commenting this change, really appreciate it! 💯

In the interest of wrapping this up soon I'll summarise the remaining action items we agreed on that I have to address:

  • Bring back the SSE transport type as it can affect adoption in a negative way
  • Rename transport_type to transport. I'd like to do that in a follow up PR thought so it's easier to review, is this okay?

Let me know if I missed something 👍

In the meantime I'll make sure to address these (bring back SSE here + the rename on top of it)

@domdomegg

Copy link
Copy Markdown
Member

I think it's those two (bring back sse, rename transport_type to transport) plus using transports as remotes directly?

Happy to do it all in one PR - as long as you keep the commits separate think it's easier to reason about as one unit.

@rdimitrov

Copy link
Copy Markdown
Member Author

I think it's those two (bring back sse, rename transport_type to transport) plus using transports as remotes directly?

Happy to do it all in one PR - as long as you keep the commits separate think it's easier to reason about as one unit.

Perfect 🙏 On it then 👍

Signed-off-by: Radoslav Dimitrov <[email protected]>
@rdimitrov

Copy link
Copy Markdown
Member Author

Alright, I've rebased the changes along with implementing the feedback items from above so it should be ready to review 👍

Comment thread internal/validators/utils.go
// Validate transport type is supported
switch transport.Type {
case model.TransportTypeStdio:
// No additional validation needed for stdio - URL should be empty

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

do we want to validate the the url is empty?

}

// validateTransport validates a remote transport (no templating allowed)
func validateTransport(obj *model.Transport) error {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

maybe rename validateRemoteTransport or something?

@domdomegg domdomegg left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

lgtm - happy to merge now and then sort out the other two things as minor follow-ups?

@domdomegg
domdomegg merged commit e0e51ea into modelcontextprotocol:main Sep 5, 2025
11 checks passed
@domdomegg

Copy link
Copy Markdown
Member

@claude can you raise two separate PRs for the review comments above? if you get stuck instead raise an issue assigned to @rdimitrov

@claude

This comment was marked as off-topic.

claude Bot added a commit that referenced this pull request Sep 5, 2025
- Add StdioTransport, StreamableHttpTransport, SseTransport schema definitions
- Add transport field to Package schema with support for all transport types
- Update remotes to use transport objects instead of old Remote schema
- Remove deprecated Remote schema
- Add 'docker' to runtime_hint examples to match server.json schema

This aligns the OpenAPI spec with the server.json schema structure
that was updated in PR #345.

Co-authored-by: adam jones <[email protected]>
domdomegg added a commit that referenced this pull request Sep 5, 2025
- Add validation that URL must be empty for stdio transport types
- Rename validateTransport to validateRemoteTransport for clarity
- Update test case to expect error when stdio transport has URL

This addresses feedback from PR #345 requesting stricter validation
for stdio transports and better function naming.

:house: Remote-Dev: homespace
@domdomegg

Copy link
Copy Markdown
Member

➡️ followups in #365

domdomegg added a commit that referenced this pull request Sep 5, 2025
## Summary
- Add validation that URL must be empty for stdio transport types
- Rename `validateTransport` to `validateRemoteTransport` for better
clarity
- Update corresponding test case to expect error when stdio transport
has URL

## Context
This addresses two specific feedback comments from @domdomegg on PR
#345:
1. "do we want to validate the the url _is_ empty?" - Now validates URL
is empty for stdio
2. "maybe rename validateRemoteTransport or something?" - Renamed
function for clarity

The changes enforce stricter validation while improving code readability
through better naming.
@rdimitrov

Copy link
Copy Markdown
Member Author

@domdomegg - thanks for addressing the comments 🙏 Funny enough I must have lost the most important commit about applying the changes on the openapi schema while I was rebasing the changes at the end 😄

@rdimitrov
rdimitrov deleted the bump-openapi branch September 8, 2025 10:40
@BobDickinson

Copy link
Copy Markdown
Contributor

Should these changes (particularly changing the Remote schema type to Transport and adding transport to the Package) have been made to the "generic" OpenAPI spec also?

https://github.com/modelcontextprotocol/registry/blob/main/docs/reference/api/openapi.yaml

I tend to use that one for generating my TypeScript client types, but noticed it doesn't have the package transport attribute.

Happy to submit a PR if so (or defer to someone with more context).

@domdomegg

domdomegg commented Sep 29, 2025 •

Copy link
Copy Markdown
Member

Yep @BobDickinson!


As a side note maybe we should add tests to keep these all in sync? Seems like it should be possible to check that the Official MCP Registry API is some superset of the generic MCP registry API spec?

➡️ #567

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.

5 participants